LINUX.ORG.RU

Racket VS Common Lisp

 , , , ,


8

10

Добрый день дорогие аналитики L0R'a. Ковыряю ракет, пишу на нем клиентскую программу - а пока хочется вот что спросить. Все же что лучше - Racket или Common Lisp? Что более перспективно? Ну и естественно, какие у одного недостатки/преимущества по сравнению с другим?

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

А почему так медленно? Почему не Racket, в котором это будет намного быстрее?

Racket у меня только стартует в десять раз дольше (последняя версия). А когда начинаешь мультиметоды писать, то он еще дольше на старте соображает... грузит свой допиленный tinyclos.

DSL vs eDSL - ну бывают случаи, когда синтаксис уже придуман и близок к одному из привычных языков. Можно конечно все записать как S-выражения, но это не всегда удобно - математические формулы, например в Maxima не в виде же S-выражений пишут. Хотя она вся на CL написана и вызывать из нее произвольный лисповский код (и наоборот) очень просто.

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

Ну так Maxima и есть eDSL внутри CL.

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

Racket у меня только стартует в десять раз дольше

Ну это прямая ложь. Racket стартует быстрее, чем SBCL.

А когда начинаешь мультиметоды писать

Какие еще мультиметоды? В Racket их нет.

Кроме того непонятно при чем тут скорость загрузки модулей вообще? Они же грузятся один раз и навсегда.

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

Просто нафиг его выкинь и напиши свой defmetod, который будет

собирать ту самую портянку из define-match автоматом.

Смысл defmethod в том, что можно написать в одном модуле класс, а потом в другом модуле описать, как этот класс должен взаимодействовать с другим классом. То есть define-match должен «собираться» непосредственно перед вызовом дженерика. Ну и порядок в define-match играет роль — нужно сортировать.

Правильная реализация — создание таблицы диспатча и отслеживание иерархии классов, топологическая сортировка — «половина Swindle».

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

Правильная реализация — создание таблицы диспатча и отслеживание иерархии классов, топологическая сортировка — «половина Swindle».

Ну сделай без топологической сортировки, пусть сортируется по порядку объявления инстанса. А таблицы никакой не надо, я же говорю - просто генерить код в define-match. Дело на сотню строк кода.

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

Ну сделай без топологической сортировки, пусть сортируется по порядку объявления инстанса.

Хм. По порядку (require ...)? Наверное, действительно, для практического применения достаточно. Описать класс до его родителя не выйдет...

monk ★★★★★
()
Ответ на: комментарий от monk
#lang racket
(require syntax/parse/define
         (for-syntax racket/format
                     racket/syntax))

(begin-for-syntax
  (struct generic (id [clauses #:mutable])
    #:property prop:rename-transformer (struct-field-index id)))

(define-simple-macro (define-generic name:id)
  #:do [(define ctx (syntax-local-context))]
  
  #:fail-unless (case ctx
                  [(top-level module module-begin) #t]
                  [else #f])
  (~a "can't be used in " (if (list? ctx) "internal definitions" ctx) " context.")
  
  #:with id (generate-temporary #'name)
  
   (begin (define id #f)
          (define-syntax name (generic #'id '()))))

(define-simple-macro (define-instance (name:id patt ...) body ...+)
  #:do [(define-values (v _) (syntax-local-value/immediate #'name))
        (set-generic-clauses! v (append (generic-clauses v) (list #'((patt ...) (body ...)))))]
  #:with (((gpatt ...) (gbody ...)) ...) (generic-clauses v)
  #:with id (generic-id v)
  (set! id (match-lambda** [(gpatt ...) gbody ...] ...)))

(define-generic yoba)

(define-instance (yoba [? integer? x] [? integer? y]) 
  (+ x y))

(define-instance (yoba [? string? x] [? string? y]) 
  (string-append x y))

это proof of concept, надо добавить нормальную обработку ошибок, еще можно сделать define-match-expander который автоматически подставлять ? или еще что в зависимости от статического связывания с символом паттерна, ну то есть можно будет писать

(define-instance (yoba [integer? x] [integer? y]) 
  (+ x y))

Ну и можно в define-generic (и в структуру generic) потом добавить какую-то еще метаинформацию, если захочешь, усложнить генерацию кода (чтоб с сортировкой и шлюхами) и т.п.

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

Что именно под этим подразумевается?

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

Что именно под этим подразумевается?

(module parent 
 ...
  (define-instance (foo (parent? x))
     ...)
)

(module child
  ...
  (define child (class parent ...))  

  (define-instance (foo (child? x))
     ...))

Потомок всегда определяется позже. Метод дженерика на нём — оже. Значит, если при выполнении будем использовать порядок обратный определению, всё должно работать корректно.

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

Не понимаю, о чем ты. Аргумент-то у функции не один, а если у нас (foo [child? x] [parent? y]) и (foo [parent? x] [child? y]) то какой инстанс вызывать? Никакого адекватного отношения порядка тут ввести нельзя, по-этому в правильной реализации мультиметодов надо писать вызовы так, чтобы подходил один единственный инстанс, остальное (если инстансов может подойти много) - должно кидать ошибку.

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

правильной реализации мультиметодов надо писать вызовы так, чтобы подходил один единственный инстанс

В таких ситуациях мультиметоды не нужны. Достаточно обычного define/match. Типовая ситуация: надо уточнять поведение на потомках, при этом желательно (но не обязательно) иметь возможность вызвать метод для предка.

(foo [child? x] [parent? y]) и (foo [parent? x] [child? y])

Такое бывает очень редко. Как правило у разных аргументов разные типы. И, соответственно, разные иерархии классов.

В идеале, конечно, defmethod на классах Racket. С сортировкой по иерархиям классов, С (call-next-method) и :around, :before и :after... С progn и and в качестве комбинаторов методов...

Но это реально половина Swindle.

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

Такое бывает очень редко. Как правило у разных аргументов разные типы. И, соответственно, разные иерархии классов.

Ну так о чем и речь. По первой иерархии надо выбирать первый метод, по второй иерархии - второй метод. Никакого способа предпочесть первый над вторым - нет.

В таких ситуациях мультиметоды не нужны.

Только в таких и нужны. Ведь в других они неприменимы by design.

Типовая ситуация: надо уточнять поведение на потомках, при этом желательно (но не обязательно) иметь возможность вызвать метод для предка.

Ну ты тут требуешь диспатча по одному аргументу - то есть обычные методы. Так что не понятно что тебе надо - обычные методы в ООП racket есть. В случае мультиметодов же мы умеем диспатчиться по типам нескольких аргументов, но теряем наследование (то есть если мультиметод определен для предка мы не имеем права определять его для потомка и наоборот). В зависимости от ситуации надо выбирать или то или другое.

Но это реально половина Swindle.

Да чушь, конечно, задача тривиальная, сотня строк, я уже говорил.

С сортировкой по иерархиям классов

ИЛИ сортировка, ИЛИ иерархия классов. Если ты вводишь иерархию классов, то ты не сможешь рассортировать инстансы. Если ты определил сортировку на инстансах, ты отменил иерархию классов (все классы становятся равные, нету потомков и предков).

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

Если интересует правильный ответ на этот вопрос, то

правильный ответ на этот вопрос я дал - это ошибка. Более приоритетного и менее приоритетного инстанса быть не может, инстанс может быть единственный. Реализация в общелиспе некорректна.

Но если хочешь, можешь сделать и некорректную сортировку, это тривиально.

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

Реализация в общелиспе некорректна.

Неужели? Как же люди 40 лет на некорректной реализации мучаются... А если серьёзно, то для достаточно широкого класса задач эта реализация очень удобна. И почему ты считаешь, что наследование по двум аргументам невозможно, я не понимаю.

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

Никакого способа предпочесть первый над вторым - нет.

Способ простой: читаем слева направо.

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

И почему ты считаешь, что наследование по двум аргументам невозможно, я не понимаю.

Какие основания предпочесть первый аргумент над вторым?

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

Почему не справа налево? Ну и ладно в общем, хочется некорректной реализации - делай некорректно, это же ничего не усложняет. В чем проблема? sort такой тривиально пишется.

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

Почему не справа налево? Ну и ладно в общем, хочется некорректной реализации - делай некорректно, это же ничего не усложняет.

В CL это всего лишь «поведение по умолчанию». Хочешь переопределить - на здоровье. Хочешь сделать правильный с твоей точки зрения вариант с выдачей ошибки - сколько угодно.

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

Да мне в Racket будет нормальные мультиметоды сделать, чем переопределять в общелиспе.

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

Racket у меня только стартует в десять раз дольше

Ну это прямая ложь. Racket стартует быстрее, чем SBCL.

Завидую... У меня наоборот:

echo "(print 1)" | time sbcl --noinform --quit --eval "(print 1)" 1

0.00user 0.00system 0:00.00elapsed

echo "(display 1)" | time /opt/bin/racket Welcome to Racket v5.3.3.

1>

0.22user 0.03system 0:00.26elapsed 98%CPU

Как разгонять старт Racket'a?

А когда начинаешь мультиметоды писать

Какие еще мультиметоды? В Racket их нет.

Здрасьте! А Swindle у вас отдельно от Racket живет? Racket именно из-за мультиметодов и хорош

Кроме того непонятно при чем тут скорость загрузки модулей > вообще? Они же грузятся один раз и навсегда.

Ну это если у вас лиспы висят как сервера и repl через сокет. Спасибо, что-то не хочется.

anonymous
()
Ответ на: комментарий от anonymous
PS C:\Users\> Measure-Command  {sbcl --eval "(quit)"}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 200
Ticks             : 2001025
TotalDays         : 2,31600115740741E-06
TotalHours        : 5,55840277777778E-05
TotalMinutes      : 0,00333504166666667
TotalSeconds      : 0,2001025
TotalMilliseconds : 200,1025

PS C:\Users\Druu> Measure-Command {racket -I racket/base -e "(exit)"}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 184
Ticks             : 1846826
TotalDays         : 2,13753009259259E-06
TotalHours        : 5,13007222222222E-05
TotalMinutes      : 0,00307804333333333
TotalSeconds      : 0,1846826
TotalMilliseconds : 184,6826



PS C:\Users\Druu>

Здрасьте! А Swindle у вас отдельно от Racket живет?

Нет, он просто не нужен, как и мультиметоды.

Ну это если у вас лиспы висят как сервера и repl через сокет.

Почему, зачем?

Вообще, не могли бы вы показать те самые 200 строк кода?

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

Более приоритетного и менее приоритетного инстанса быть не может, инстанс может быть единственный.

А как сделать условие на «элемент принадлежит классу и не принадлежит ни одному из потомков класса»? В CL такое делается через (eq (class-of instance) my-class). А в Racket?

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

А как сделать условие на «элемент принадлежит классу и не принадлежит ни одному из потомков класса»?

Так же.

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

Так же.

В Racket нету ни type-of, ни class-of. is-a? возвращает #t как для объекта класса, так и для объекта любого потомка класса.

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

object-info возвращает класс объекта.

the result is #f if the current inspector does not control any class for which the object is an instance — это для дебаггеров.

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

the result is #f if the current inspector does not control any class for which the object is an instance — это для дебаггеров.

Ну естественно. А как иначе-то? Это же прямое нарушение инкапсуляции и семантики языка. Не должно быть возможностей написать ф-ю, которая принимает суперкласс но не принимает его подкласс. Любая ф-я подкласс обязана принимать - это суть семантики подтипирования. Если вдруг тебе требуется в твоей логике применять дебажные ф-и - то это быдлокод и ошибка архитектуры.

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

Нет, можешь миксин сделать. Только вот далеко не у любого класса есть имя, классы и анонимные бывают (те, которые получаются в результате применения миксина, например).

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

Только вот далеко не у любого класса есть имя, классы и анонимные бывают

Мда... на этой мысли я пошёл перечитывать доки по Racket. Это покруче, чем CLOS-овская идея про «методы отдельно, классы отдельно». Хотя, учитывая любовь Scheme'ров к лямбдам, всё закономерно. Но воспринимать надо тогда не с позиций C++ или CLOS а с «чистого листа».

Спасибо за помощь в понимании.

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

Кстати аналог allocation :class сделать можно:

(define test% 
  (let ([%foo null])
    (class object% 
      (init) 
      (super-new)
      (define/public (foo) %foo)
      (define/public (set-foo! val) (set! %foo val)))))

(define bar (new test%))
(define baz (new test%))

(send bar set-foo! 1)
(send baz foo) => 1
monk ★★★★★
()
Ответ на: комментарий от monk

Мда... на этой мысли я пошёл перечитывать доки по Racket.

Да собственно в чем сложность? Класс - обычное значение, ф-и могут принимать классы, возвращать, генерировать внутри себя и т.п.

Разве в CLOS не так?

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

Кстати аналог allocation :class сделать можно:

А, я просто подумал, что allocation это нечто другое.

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

Разве в CLOS не так?

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

Так и в CLOS. Класс — это идентификатор для определения того, какой из методов обобщенной функции применить. И мышление строится именно в этом ключе: иерархии классов, доопределение методов на классах-потомках. А в Racket класс, по-факту, просто замыкание. У него есть интерфейс, но нет идентичности. Непривычно.

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

А еще там augmenting есть.

А вот смысл этого я не понял. В смысле, в чём разница между

(define buzzer%
  (class object%
    (super-new)
    (define/pubment (buzz)
      (displayln "bzzzt")
      (inner (void) buzz))))

и

(define buzzer%
  (class object%
    (super-new)
    (define/public (buzz)
      (displayln "bzzzt")
      (inner-buzz))
    (define (inner-buzz)
      (void)))))

?

В первом случае будет augment, во втором — переопределение inner-buzz. Или я что-то упустил?

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

Оригинальная штука. Опять идеология с ног на голову: «разработчик субкласса знает лучше» для меня всегда было почти аксиомой.

monk ★★★★★
()

топег не читал, кратко спрошу: клиентская программа на Racket это как? какой GUI для маргинальных языков типа Lisp?

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Ракет использует gtk на линух и native API на остальных системах.

x4DA ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

ну и есть CommonQT и другие для общелиспа и лиспы под JVM и CLR

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

чем? не то, чтобы я не согласен, но все-таки?

x4DA ★★★★★
()
24 октября 2013 г.
12 сентября 2014 г.

Перечитал тред полуторагодичной давности, поностальгировал, прослезился. Какой же это всё-таки пир духа — срач лисперов со схемерами!

Знаете, это напоминает мне классический французский анекдот. Кажется, на ЛОРе я его ещё не рассказывал.

Парижская жандармерия. В кабинет капитана врывается взмыленный жандарм:

Жандарм:
— Месье! В 14-м округе педерасты дерутся с проститутками!
Капитан (аккуратно подпиливая ногти пилочкой):
— Ну, и как там наши?..

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