LINUX.ORG.RU

Racket


1

1

В последнее время играюсь к clojure, на этой волне посмотрел, что такое сабж. Понравилось. Вопрос - что вы думаете об этом ЯП? Какие у него перспективы в плане использования для высокоуровневой разработки компилируемых в нативный код программ? Т.е. производительность, удобство разработки, инфраструктура, количество библиотек, графических тулкитов и т.д.? В какой области у него наибольшие преимущества?

★★★

> Какие у него перспективы в плане использования для высокоуровневой разработки компилируемых в нативный код программ?

Насколько знаю, там байт-код. Плюс в исполняющей среде нет встроенного компилятора. Пусть меня поправят.

dave ★★★★★ ()
Ответ на: комментарий от dave

Насколько знаю, там байт-код.

Там JIT

Плюс в исполняющей среде нет встроенного компилятора.

Есть.

anonymous ()

В какой области у него наибольшие преимущества?

кластера метапарадигм, очевидно. Это же лисп.

anonymous ()
Ответ на: комментарий от dave

Насколько знаю, там байт-код. Плюс в исполняющей среде нет встроенного компилятора. Пусть меня поправят.

Ну штука, которая делает исполняемые файлы, там есть - raco. Просто непонятно, во что именно оно компилирует. Причем хелловорлд компилится в несколько мб и запускается 0,5 с, так что рантайм там немаленький, возможно и компилятор есть.

ovk48 ★★★ ()
Ответ на: комментарий от ovk48

Причем хелловорлд компилится в несколько мб и запускается 0,5 с, так что рантайм там немаленький, возможно и компилятор есть.

Компилируется оно в байткод, в скомпилированном бинарнике - байткод всех импортированных модулей (то есть для #lang racket это будет вся стандартная библиотека, например), если ограничиться '#%kernel, то там и десятка кб не будет, скорее всего.

anonymous ()
Ответ на: комментарий от anonymous

компилятор там встроенный, причем полностью встроенный. абсолютно как в CL. Не стоит заниматься анонимных пиздежом, если не разбираешься в теме разговора.

anonymous ()
Ответ на: комментарий от anonymous

Ну в схемах это всегда так, впрочем.

Уровень анонимной икспертизы просто зашкаливает.

anonymous ()
Ответ на: комментарий от anonymous

Знаешь, одно может жить без другого. Подобие REPL есть в F#, Ocaml, Scala и Haskell. Но нигде там не встраивается компилятор в исполняемый код. По крайней мере, нет простых способов.

dave ★★★★★ ()
Ответ на: комментарий от anonymous

Ну видишь ли, нет, не встроенный. Или, если ты такой уж упертый - он там такой же встроенный, как и компилятор C# встроенн в фреймворк.

И вообще, в схемах отделение рантайма и времени трансляции всегда было, и есть, очень строгое.

anonymous ()
Ответ на: комментарий от dave

Но нигде там не встраивается компилятор в исполняемый код.

Что значит «встраивается в исполняемый код»? Это какое-то, на мой взгляд, бессмысленное утверждение.

anonymous ()
Ответ на: комментарий от anonymous

он там такой же встроенный, как и компилятор C# встроенн в фреймворк.

Ах, так вот о чем речь! Ну да - в сабже рантайм находится не в самом бинарнике, а в отдельной дллке. Это что-то существенно меняет?

И вообще, в схемах отделение рантайма и времени трансляции всегда было, и есть, очень строгое.

Опять бестолковая анонимная икспертиза. При наличие репла такие понятия как «время трансляции» вообще не имеют никакого смысла, потому что оно же является и временем исполнения. И разделить их никакой возможности нет.

anonymous ()
Ответ на: комментарий от anonymous

Давай зайдем с другой стороны. Что можно сделать с компилятором CL/при помощи компилятора CL, чего нельзя сделать с компилятором racket/при помощи компилятора racket?

anonymous ()
Ответ на: комментарий от anonymous

И разделить их никакой возможности нет.

К слову, в отличии от CL, где любой код относится к какому-то конкретному этапу (чтение, компиляция, загрузка, исполнение и т.д.) и все информация об этом коде после исполнения этапа исчезает, в racket такого нет - один и тот же код может работать в любой фазе и вся информация остается, поддерживая, таким образом консистентность рантайма..

anonymous ()
Ответ на: комментарий от anonymous

Это в CL компиляция и есть время исполнения. И поэтому там REPL полноценный.

А в схемах repl это просто один большой toplevel.

Нет, разница не только в том что «в отдельной dll'ке»(LW/ACL тоже могут в отдельную DLL-ку пихать).

Разница в том, что это компилятор файлов и модулей. Отдельная сторонняя сущность, а не часть рантайма лисп-системы.
Вообще в контексте схем - никакой лисп-системы нет. Есть «программы». Как в контексте Си и прочих «традиционных» «компилируемых» языков.

anonymous ()
Ответ на: комментарий от anonymous

Это в CL компиляция и есть время исполнения.

В racket тоже. Следовательно - в racket repl тоже полноценный. Так?

А в схемах repl это просто один большой toplevel.

что здесь подразумевается под «одним большим топ-левелом»?

Разница в том, что это компилятор файлов и модулей.

Нет, это не компилятор файлов и модулей. Это такой же eval/compile, как в CL.

Отдельная сторонняя сущность, а не часть рантайма лисп-системы.

Нет, это не отдельная сущность. Это обычные ф-и (eval/compile) обычного лисповго рантайма. То есть - часть рантайма, да.

Вообще в контексте схем - никакой лисп-системы нет.

В схемах, конечно, все по-разному. Если говорить о racket - то там есть лисп-система и любая программа выполняется в ходе работы этой лисп-системы. Запуска модуля = запуск лисп-системы, загрузка файла с модулем, исполнение модуля, завершение работы лисп-системы.

Есть «программы».

Нет, есть лисп-система и ее состояние.

anonymous ()
Ответ на: комментарий от anonymous

1) В racket нет compile-file

2) Ты не понял.
foo мы не загружаем, а компилируем(причем компилируем при загрузке bar, при вызове compile-file, а не откуда-то еще какими-то сторонними методами). А bar загружаем.
Таким образом, рантайм и compile-time неотделимы.
У тебя все в compile-time происходит.

anonymous ()
Ответ на: комментарий от anonymous

1) В racket нет compile-file

Ну да, впрямую нет, но есть with-open-file и read, который вернет sexpr, и есть compile, который, будучи применен к этому sexpr'у, вернет байткод формы.

2) Ты не понял.

Нет, это ты несколько не понял. В твоем представлении у нас буд-то бы есть две разных лисп-машины, одна из которых работает пока мы компилируем, а вторая - когда запускаем программу. Так это не так. Лисп-машина одна и состояние одно, ну для того чтобы тебе было понятно - вот как в CL для символа есть слоты значения, ф-и, класса, етц. и все эти слоты существуют одновременно, в одном рантайме, так и здесь есть для каждого символа набор слотов - по слоту на каждую фазу, и все эти слоты существуют в рамках одного рантайма, единомоментно. И вся информация, что была получена во время компиляции - она никуда не проебывается, она и во время рантайма остается, потому что это одна лисп-машина и одно состояние. Вот тебе пример по-лучше: [code]

(module m racket

(define-namespace-anchor nsa) (define ns (namespace-anchor->namespace nsa)) (begin-for-syntax (define x 5) (displayln 'phase1)) (displayln 'phase0) (eval '(begin-for-syntax (display x)) ns)) phase1

(require 'm)

phase1 phase0 5


[/code] eval выполняется в рантайме, то есть когда модуль уже скомпилирован и обратиться к состоянию компайл-тайма нам ничего не помешало. А можно и так: [code] #lang racket

(define-namespace-anchor nsa) (define ns (namespace-anchor->namespace nsa))

(define-syntax (get stx) (syntax-case stx () [(_ id) #`#,(eval #'id)]))

(define-syntax (set!! stx) (syntax-case stx () [(_ id expr) #`#,(eval #'(set! id expr))]))

(begin-for-syntax (define x 5))

(eval '(get x) ns) (eval '(set!! x 100) ns) (eval '(get x) ns)

-> 5 100


[/code] опять же - евал выполняется в рантайме, что не мешает нам менять компайл-тайм состояние

anonymous ()
Ответ на: комментарий от anonymous
> (module m racket
    (define-namespace-anchor nsa) 
    (define ns (namespace-anchor->namespace nsa)) 
    
    (begin-for-syntax 
      (define x 5) 
      (displayln 'phase1)) 
    
    (displayln 'phase0) 
    (eval '(begin-for-syntax (display x)) ns))
phase1
> (require 'm)
phase1
phase0
5
> 

и

#lang racket

(define-namespace-anchor nsa)
(define ns (namespace-anchor->namespace nsa))

(define-syntax (get stx)
  (syntax-case stx ()
    [(_ id) #`#,(eval #'id)]))

(define-syntax (set!! stx)
  (syntax-case stx ()
    [(_ id expr) #`#,(eval #'(set! id expr))]))

(begin-for-syntax 
  (define x 5))

(eval '(get x) ns)
(eval '(set!! x 100) ns)
(eval '(get x) ns)

->
5
100
> 

anonymous ()
Ответ на: комментарий от anonymous

Я не так много имел ввиду. Просто сравнивал с CL. Компилятор можно подключить в некоторых случаях - не вопрос. Вон, один товарищ это сделал для Scala. Но по его собственному признанию ему пришлось немного помучаться в поисках ответов на StackOverflow.

В CL не так. Там компилятор уже встроен в каждую лисп-систему. И он может на лету генерировать код (нативный для SBCL) прямо в работающий образ. И как намекает другой аноним, оказывается, что все не так-то просто. Я честно говоря, не особо вдавался в дебри EVAL-WHEN, но, думаю, что от CL отодрать компилятор очень и очень непросто (интересно, умеет ли это делать LispWorks?). Что-то в этом роде имелось ввиду. И только.

dave ★★★★★ ()
Ответ на: комментарий от dave

Там компилятор уже встроен в каждую лисп-систему. И он может на лету генерировать код (нативный для SBCL) прямо в работающий образ.

Ну в racket также, только код будет не нативный, а байткод (в нативный джитится при первом вызове ф-и):

> (define sexpr '(λ () 'compiled))
> (define compiled (compile sexpr))
> (define f (eval compiled))
> sexpr
'(λ () 'compiled)
> compiled
*непечатные кракозябры*
> f
#<procedure>
> (f)
'compiled
> 

anonymous ()
Ответ на: комментарий от anonymous

Это сеанс REPL. А такое возможно во время исполнения «бинарника»? Просто, когда я создавал тестовые бинарники в Racket, то меня сильно смущал их размер (уже не помню, но где-то мегабайта четыре). И туда умещается компилятор самого Racket?

dave ★★★★★ ()
Ответ на: комментарий от dave

Бинарники в racket бывают трех видов: 1. байт-код модуля + стартер. в этом случае бинарники маленькие, в них содержится, собственно, байткод скомпилированного модуля (без зависимостей) и враппер, который запускает рантайм, делает require модуля, вырубает рантайм. семантика исполнения в этом случае вообще не отличается от того, как если бы мы делали обычный require в репле (потому что по сути стартер это и делает). 2. стартер + модуль + зависимости как в первом случае, но кроме байткода модуля в файл кладется еще и байткод всех импортированных модулей, семантика может измениться в случае компайл-тайм сайд-эффектов и т.п.., размер существенно зависит от того, какие библиотеки импортированы (для чистого #lang racket/base будет ~600кб, для чистого #lang racket - 3.5мб) 3. в первых двух вариантах используется установленная в системе вм, а не ее копия, вот в третьем - к бинарнику из пункта 2 добавляются все нужные для рантайма библиотеки, в этом случае бинарник запускает именно эту копию вм и не зависит от того, установлен ли она в системе. размер - бинарник как в п.2 + 3.5мб рантайм. семантику загрузки модулей п.2/3 при помощи dynamic-require можно свести при желании к п.1.

anonymous ()
Ответ на: комментарий от dave

А такое возможно во время исполнения «бинарника»?

Ну если ты запустил репл - он запустится. Если не запускал - не запустится.

anonymous ()
Ответ на: комментарий от anonymous

Вход:

#lang racket

(define sexpr '(λ () 'compiled))
(define compiled (compile sexpr))
(define f (eval compiled))

(display (f))

Ошибка:

compile: unbound identifier (and no #%app syntax transformer is bound) in: λ

Что ему не нравится?

dave ★★★★★ ()
Ответ на: комментарий от CARS

Все уже сказали. Насчет работы compile из бинарников не знаю, не пользовался ни разу, но с read/eval/apply проблем не возникало.

Reset ★★★★★ ()
Ответ на: комментарий от dave

eval вычисляет выражение в заданном неймспейсе, который передается вторым аргументом. Если аргумент не указан, используется параметр current-namespace. В top-level current-namespace - это неймспейс топ-левела, в module-level - это пустой неймспейс во избежание неоднозначностей. С каждым модулем связан свой собственный неймспейс, соответственно, чтобы вышеприведенный код работал - мы должны получить неймспейс текущего модуля и делать eval в нем. С compile чуть сложнее - неймспейс туда явно не передается, так что поведение можно изменить только переопределяя параметр. В итоге получаем:

#lang racket

(define-namespace-anchor nsa) ;хук к модулю
(define ns (namespace-anchor->namespace nsa)) ;получаем неймспейс

(current-namespace ns) ;устанавливаем неймспейс

(define sexpr '(λ () 'compiled))
(define compiled (compile sexpr))
(define f (eval compiled))

(display (f))

->
compiled
> 

anonymous ()

Т.к. тема опять ушла в обсуждение анафорических макросов (тм), апну и предложу все-таки поговорить на тему:

Какие у него перспективы в плане использования для высокоуровневой разработки компилируемых в нативный код программ? Т.е. производительность, удобство разработки, инфраструктура, количество библиотек, графических тулкитов и т.д.? В какой области у него наибольшие преимущества?

ovk48 ★★★ ()
Ответ на: комментарий от ovk48

Ну а чего тут обсуждать? Такой же лисп, как и остальные лиспы, в этом плане «производительность, удобство разработки, инфраструктура, количество библиотек, графических тулкитов и т.д.» - как у всех лиспов. Основная особенность - макросы, сделанные правильно. Отсюда - позиционируется как фреймворк для написания своих ЯП/ДСЛей, короче, во всю пропагандирует dsl-driven development и предоставляет полноценное окружение для этого. Для соответствующих задач и будет удобен. Больше и нечего добавить.

anonymous ()

В последнее время играюсь к clojure, на этой волне посмотрел, что такое сабж

Посмотри лучше kawa.

no-such-file ★★★★★ ()
Ответ на: комментарий от anonymous

Ну не совсем, гравический тулкит все-таки довольно неплохой, почти как LispWorks CAPI (не по подходу, а по «батарейковости»).

А

Основная особенность - макросы, сделанные правильно. Отсюда - позиционируется как фреймворк для написания своих ЯП/ДСЛей

не совсем верно, там дело далеко не в макросах.

korvin_ ★★★★★ ()
Ответ на: комментарий от korvin_

не совсем верно, там дело далеко не в макросах.

Естественно - дело не только в макросах, но и в самом окружении, концепции #lang'ов, фазах и так далее. Но в совокупности это все и называется «макросы (и обычные и переопределение ридера/поведения рантайма) сделанные правильно».

anonymous ()
Ответ на: комментарий от korvin_

Ну и, кстати, это единственный известный мне лисп, в котором есть _нормальный_ макростеппер, благодаря которому макросы реально легко и приятно отлаживать.

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.