LINUX.ORG.RU

Не учите Common Lisp!

 ,


1

7

Хочу предостеречь форумчан от изучения Common Lisp. Возьмите самую лучшую имплементацию — SBCL и скомпилируйте следуюущие лямбды:

;; Взятие элемента по индексу x из массива x
(lambda (x) (aref x x))
;; Взятие элемента по индексу y из массива x и прибавление к x
(lambda (x y) (+ x (aref x y)))
;; Один из путей выполнения: прибавление X к NIL
(lambda (x y) (+ x (funcall (lambda (x) (if x 0 x)) y)))

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

Минус коммон лиспа как языка — слишком разрешательский стандарт. Почти сплошь и рядом там фразы вроде «Эффект, производимый объявлением inline, определяется реализацией» или «Реализация вольна игнорировать то-то и вместо этого делать то-то». Из-за этого разработчики того же SBCL будут игнорировать твои баг репорты, где из-за неправильного поведения компилятора (с точки других опубликованных алгоритмов) твой код будет работать раза в 2 или 3 медленнее, чем должен бы.

Написали бы в стандарте «реализация ДОЛЖНА поступать так-то и так-то», и можно было бы тыкать разрабов носом в это место и требовать корректного поведения. А раз написали «реализация может делать, что ей захочется», то и прижучить их нельзя. В итоге все занимаются чем хотят, кроме починки багов.

Короче, учите языки с хорошей теоретической базой и статической типизацией. Байкам @lovesan не верьте ни одной.



Последнее исправление: dura4ok11 (всего исправлений: 1)
Ответ на: комментарий от dura4ok11

А читать не надо.

Вот этим и отличаются динамические языки от статических. На динамическом ты просто пишешь программу, а когда она уже работает, смотришь, где её можно ускорить. В статическом тебе на каждом шагу компилятор рассказывает, чего тебе не надо. Начиная от нормальной арифметики (как в старых алголоподобных), заканчивая тремя ущербными видами списков вместо одного нормального.

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

Ты вот про Хаскель, а я про АНДЕРГРАУНД.

Вот статейка:

https://www.researchgate.net/publication/220997326_A_General_Scheme_for_the_Automatic_Inference_of_Variable_Types

Там проблема, что для каждой БАЗОВОЙ функции от n агрументов нужно определять аж n+1 дополнительных функций, которые будут помогать выводить типы. Это конечно гемор, в отличие от simply typed лямбда калькулюса какого-нибудь.

Если хочешь, можешь поиграться тут: https://github.com/shamazmazum/type-inference-engine

Я реализовал этот алгоритм для очень простой системы типов и нескольких простейших функций. И что удивительно:

CL-USER> (type-inference-engine/example:infer-types '(elt x x))
((#:RES245 . #<TYPE-INFERENCE-ENGINE:TYPE-NODE NIL {1113511383}>)
 (X . #<TYPE-INFERENCE-ENGINE:TYPE-NODE NIL {1113511383}>))
#:RES245
CL-USER> (type-inference-engine/example:infer-types '(elt x y))
((#:RES260 . #<TYPE-INFERENCE-ENGINE:TYPE-NODE T {1113511503}>)
 (X . #<TYPE-INFERENCE-ENGINE:TYPE-NODE SEQUENCE {1100CB84D3}>)
 (Y . #<TYPE-INFERENCE-ENGINE:TYPE-NODE INTEGER {1113511483}>))
#:RES260
dura4ok11
() автор топика
Ответ на: комментарий от monk

А у HList обратная проблема.

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

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

Вот этим и отличаются динамические языки от статических. На динамическом ты просто пишешь программу, а когда она перестаёт падать от ошибок типов…

Починил.

Как это работает на практике: ты запускаешь прогу на пердоне и она падает со блевотиной стектрейса из-за того, что NoneType не имеет нужного метода.

В статическом тебе на каждом шагу компилятор рассказывает, чего тебе не надо.

Ты просто не умеешь его готовить. У кучи программистов есть такая штука, что они воспринимают компилятор как своего врага, который им мешает. Особенно у сишников эта шиза часто встречается (им компилятор UB в код суёт), но не только.

В хачкелле гораздо удобнее начать писать код с моделирования типов, а потом подставлять уже код в функции, подходящий по типам. А не наоборот.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)

@monk Если заинтересуешься, вот система типов, которую я использую в примере к движку:

https://shamazmazum.github.io/type-inference-engine/type-system.html

Там такое же подтипирование, как в коммон лишпе, но нет compound types.

Вот тут базовые функции, которые поддерживаются примером: https://github.com/shamazmazum/type-inference-engine/blob/master/example/example.lisp

Пробовать так: (type-inference-engine/example:infer-types 'expression),

например

CL-USER> (type-inference-engine/example:infer-types '(if z (+ x (floor (elt y x))) (1+ x)))
((Y . #<TYPE-INFERENCE-ENGINE:TYPE-NODE SEQUENCE {1100CB84D3}>)
 (#:VAR277 . #<TYPE-INFERENCE-ENGINE:TYPE-NODE NUMBER {11135114C3}>)
 (#:VAR276 . #<TYPE-INFERENCE-ENGINE:TYPE-NODE INTEGER {1113511483}>)
 (X . #<TYPE-INFERENCE-ENGINE:TYPE-NODE INTEGER {1113511483}>)
 (#:RES273 . #<TYPE-INFERENCE-ENGINE:TYPE-NODE INTEGER {1113511483}>)
 (Z . #<TYPE-INFERENCE-ENGINE:TYPE-NODE BOOLEAN {1100CB8513}>)
 (#:VAR274 . #<TYPE-INFERENCE-ENGINE:TYPE-NODE INTEGER {1113511483}>)
 (#:VAR275 . #<TYPE-INFERENCE-ENGINE:TYPE-NODE INTEGER {1113511483}>))
#:RES273

Тут возвращает отображение «переменная — самый узкий безопасный тип». Второе возвращаемое значение — переменная с результатом.

dura4ok11
() автор топика
Ответ на: комментарий от hateyoufeel

Только типы надо ограничивать теми, что доступны в Хаскеле. Тогда всë хорошо. А если пытаешься переписать с лиспа или си++ и оставить те типы, что есть, то будет сплошная борьба с компилятором.

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

Как это работает на практике: ты запускаешь прогу на пердоне и она падает со блевотиной стектрейса из-за того, что NoneType не имеет нужного метода.

Да, и от этого нервы. Хочется крушить и ломать. На лишпе такого меньше, потому что sbcl какие-то ошибки всё же ловит, но тоже не редко случается.

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

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

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

Только типы надо ограничивать теми, что доступны в Хаскеле

Есть data и newtype

А вот в коммон лиспе так и есть, как ты сказал.

(serapeum:defconstructor
  (x (simple-array ???? (*)))

Что мне ставить на место ???? ?

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

Притом что это две стороны одной медали.

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

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

Для обезьяны поясню очень кратко: Значение 1 в лишпе может одновременно иметь тип (eql 1), (member 1 3), (integer -1 10), bit, fixnum, integer, unsigned-byte, number или t (это малый только набор).

Если ты передаёшь его в функцию, что именно функция ожидает?

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

Только типы надо ограничивать теми, что доступны в Хаскеле. Тогда всë хорошо.

Я нихрена не понял, что это значит.

А если пытаешься переписать с лиспа или си++ и оставить те типы, что есть, то будет сплошная борьба с компилятором.

И вот это.

hateyoufeel ★★★★★
()

tl;dr

а declaim и правильные типы прописать – конечно, слабо?

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

Значение 1 в лишпе может одновременно иметь тип (eql 1), (member 1 3), (integer -1 10), bit, fixnum, integer, unsigned-byte, number или t (это малый только набор).

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

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

на сайте hh.ru не отображаются вакансии с требованием LISP

зато они отображаются в платной версии сайта. лисперов берут аниматорами в турцию и выгуливателями крокодилов в тунис.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от dura4ok11

Для обезьяны поясню очень кратко

Зачем вы объясняет то что и так известно?

Если ты передаёшь его в функцию, что именно функция ожидает?

вопросы здесь задаю я (с)

Давайте вы лучше поведаете про упомянутое вами «подтипирование» :)

Статью прочитай сначала

лень, честно. Я не могу на серьёзных щщах обсуждать необходимость n+1 функций для n аргументов.

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

В коммон лиспе через deftype можно любой тип описать. А через макросы любые проверки при компиляции.

А в Хаскеле для проверок всë равно лучше внешний линтер прикручивать типа Liquid Haskell.

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

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

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

К макросам тоже есть вопросики.

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

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

Я нихрена не понял, что это значит.

Например, хочешь реализовать Cons-ячейку. В смысле, пару элементов разных типов.

То есть то, что на лиспе делается тривиально

(defstruct cons (car cdr))

Тип вроде очевиден:

data Cons a b = Cons a b

Потом берём алгоритм построения списка из этих ячеек. Вводим специальный элемент end. В лиспе это символ, в Хаскеле

data End = End

Всё вроде хорошо

head (Cons x y) = x
tail (Cons x y) = y

Теперь надо реализовать расчёт длины

(defun len (cons)
  (if (eq (cons-cdr cons) 'end)
    1
    (+ 1 (len (cons-cdr cons)))))

И тут выясняется, что эту функцию написать невозможно, несмотря на то, что её тип точно определён (Сons a b -> Int).

То есть она должна была бы быть такой

len (Cons _ End) = 1
len (Cons x y) = 1 + len y

но это невозможно в системе типов Хаскеля.

Выкидываем всё написанное, начинаем читать про HList.

Потом для каждого диапазонного типа, который нужен, реализуем арифметику, для каждой ограниченной строки реализуем все классы списков… или забиваем на всё это и вместо диапазона пишем Integer или Float (неважно, что не бывает отрицательного количества, а доля в процентах не может быть выше 100), вместо ограниченной строки пишем неограниченную (и стараемся не забыть проверку в рантайме), а тот же Cons реализуем не как Cons a b, а как data Cons = Cons Dynamic Dynamic.

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

В Haskell тоже падает.

В то время, как даже в C++, не говоря уж о лиспе, вполне можно проверить соответствие типов.

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

Ущербно перезапускать программу после падений из-за несоответствия типов

Ущербно не запустить программу ни разу из-за несоответствия типов.

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

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

Это реализация расширений компилятора. Тот же Racket позиционируется как язык для написания DSL (помимо макросов предлагается расширения для замены парсера на свой). Другая крайность того же самого — JVM и LLVM.

Но у макросов есть то удобство, что их можно воткнуть по месту. Как функции заменяют куски исполняемого кода, так макросы заменяют куски произвольного кода (функцией обычно нельзя описать тип или создать функцию). А там, где можно (как в Zig), там макросы становятся не нужны.

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

То есть то, что на лиспе делается тривиально

(defstruct cons (car cdr))

Тип вроде очевиден:

data Cons a b = Cons a b

Потом берём алгоритм построения списка из этих ячеек. Вводим >специальный элемент end. В лиспе это символ, в Хаскеле

data End = End

Ну, то есть, ты решил сделать через задницу и жалуешься, что получается через задницу?

И тут выясняется, что эту функцию написать невозможно, несмотря на то, что её тип точно определён (Сons a b -> Int).

Потому что для любых a и b такая функция и правда невозможна. У тебя огромные проблемы в логике здесь, но ты пытаешься их выдавать за недостатки хачкеля.

data End = End

data Cons a b = Cons a (Either End b)

type List a = Cons a (List a)

len :: List a
len (Cons _ (Left End)) = 1
len (Cons _ (Right l)) = 1 + len l

Кстати, твой список не может быть пустым по определению, но это детали.

Выкидываем всё написанное, начинаем читать про HList.

data List a = Cons a (List a) | Nil

len :: List a -> Word
len Nil = 0
len (Cons _ l) = 1 + len l

Вообще не понимаю твоей проблемы тут.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 3)

Все проблемы Хаскеля — это то, что нужно учить Хаскель! Я так щитаю

dura4ok11
() автор топика

А вообще на редкость говёный срач вышел. 90% людей вообще не понимают, о чём речь. Пойду ща баниться

dura4ok11
() автор топика
Ответ на: комментарий от hateyoufeel

Да есть, я ж писал. НО МАЛО

ЗАТО вот смотри, какая у меня прога для плетения жгутов! https://github.com/shamazmazum/cl-beads

К слову, на хацкеле тоже только git-annex, pandoc и xmonad

dura4ok11
() автор топика
Последнее исправление: dura4ok11 (всего исправлений: 2)
Ответ на: комментарий от hateyoufeel

Вообще не понимаю твоей проблемы тут.

Тест

(let ((a (cons 5 (cons "ok" (cons 2.5 'end)))))
  (test-= (len a) 3))

Теперь не работает. В лиспе работал, должен работать.

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

Ни одной программы на лиспе нету, зато гонора – вагоны.

Genera OS, Maxima.

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

К макросам тоже есть вопросики.

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

Ну а как же. Реальность ведь меняется, наше понемание реальности меняется тоже — значит, и язык должен уметь меняться следом.

В лишпах для этого не нужно быть в команде разработчиков языка, достаточно уметь писать функции — это плюс.

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

Ни одной программы на лиспе нету, зато гонора – вагоны.

Любая достаточно сложная программа содержит заново написанную, неспецифицированную, глючную и медленную реализацию половины языка Common Lisp

(c)

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

Теперь не работает. В лиспе работал, должен работать.

Кому должен-то? Ты пытаешься написать список как на лиспе но на хачкелле, у тебя не получается, в итоге плохой почему-то хачкель. Очень странный подход к аргументации.

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

Любая достаточно сложная программа содержит заново написанную, неспецифицированную, глючную и медленную реализацию половины языка Common Lisp

Это лисповый копиум ничего кроме усмешек давно не вызывает.

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

Я ж тебе объяснил про экзистенциальные типы.

Если тебе просто посчитать кол-во элементов, тебе и контекст не нужен

data List = forall a. Cons a List | Nil

Ты можешь хоть список из боттомов сделать, для вычисления длины сами элементы не нужны

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

data List = forall a. Cons a List | Nil

Этот «список» уже предлагали. Из него элементы не вытаскиваются.

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

Ты пытаешься написать список как на лиспе но на хачкелле, у тебя не получается, в итоге плохой почему-то хачкель.

Естественно. Он позволяет определить структуру с полями разных типов, но не позволяет на этой структуре использовать некоторые алгоритмы списка. Что я и писал выше: типы надо ограничивать теми, что доступны в Хаскеле. Тогда всë хорошо.

data Cons a b = Cons a (Either End b)

Тут получается, что может быть Cons 42 (Left End) и Cons 42 (Right End). Что значит второе — непонятно.

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

Ты пытаешься написать список как на лиспе но на хачкелле, у тебя не получается, в итоге плохой почему-то хачкель.

В итоге всё-таки получилось:

data Cons a b = Cons a b
data End = End

class MyList a where
  len :: a -> Int

instance MyList End where
  len _ = 0

instance (Len b) => MyList (Cons a b) where
  len (Cons _ y) = 1 + len y

main = do
  putStrLn . show . len $ Cons 5 $ Cons "ok" $ Cons 2.5 End

И даже без ужасов HList’а. Но времени на борьбу с типами потратилось примерно вчетверо больше, чем на сам алгоритм.

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

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

Чо?

Что я и писал выше: типы надо ограничивать теми, что доступны в Хаскеле.

Я всё ещё не понимаю значения этой фразы.

Тут получается, что может быть Cons 42 (Left End) и Cons 42 (Right End). Что значит второе — непонятно.

Если тебе непонятно, то зачем ты это написал?

В итоге всё-таки получилось:

Поздравляю. Ты… изобрёл примитивную реализацию HList!

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от monk

Но времени на борьбу с типами потратилось примерно вчетверо больше, чем на сам алгоритм.

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

Повторюсь: ты решил делать через жопу и удивляешься, что у тебя код сделан через жопу. Кто в этом виноват-то? Ты ещё пожалуйся, что хачкель не позволяет писать как на Фортране или что нельзя получить указатель на любое из значений, как это делается в C.

Если тебе непонятно, то зачем ты это написал?

Я? Это ты здесь написал: Не учите Common Lisp! (комментарий)

Да, ты. Я перевёл твой странный код с лиспа на хачкель и всё. Идея целиком твоя.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Чо?

То. Пишу car, работает, пишу cdr, работает. Пишу, length, не работает.

Я всё ещё не понимаю значения этой фразы.

Ну нету в Хаскеле ни диапазонных типов, ни ограниченных списков (в смысле, «строка до 50 символов»), ни подклассов (в результате GUI с ужасным API). Если это всё не надо, то всё хорошо. Как только становится надо, начинаешь на каждый тип закатывать Солнце вручную.

Полдюжины библиотек для работы с вариантными типами намекают.

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

Я перевёл твой странный код с лиспа на хачкель и всё. Идея целиком твоя.

Не было в моей идее никакого Either. Он там лишний.

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

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

А если типы значений заранее неизвестны? Зачем для алгоритма, вычисляющего длину списка тип значения элемента списка?

monk ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.