Не знаю вообще ни одного функционального языка. С какого легче начать обучение - Haskell или Lisp. И, если Lisp, то какой самый распространенный интерпретатор?
>Если это про pattern matching, то если ты не заметил лисповый вариант куда мощьнее хаскелльного. Например ты можешь в хаскелле сматчить s-expression?
Именно сматчить легко, а если ты потом еще хочешь с ним работать как с кодом то тогда надо Template Haskell поюзать... Ниче там не мощнее - lazy matching же не сделаешь... Вот так оно может?
>> По мне так это велосипедизм, вместо одной стандартной реализации - зоопарк костылей... А библиотек можно и в Haskell'е наплодить...
В хаскелле тоже не одна реализация - это раз. Во вторых диалектов лиспа дофига и если говорить о стандарте без зоопарка то Common Lisp тебе в руки. Там стандартизовано (именно стандартизовано, ANSI и все такое) столько, что Хаскелю и не снилось. В третьих все что реализовано в "стандартной реализации" GHC (которая далеко не стандартная а зоопарк расширений) - это тоже набор костылей к Haskell 98. Так что не надо грязи.
>> Списки и деревья это примеры рекурсивных типов, но есть и другие
imp, если своими словами, то тип можно построить при помощи двух операций: неперсекающегося объединения и декартова произведения. Например, новый тип из двух других можно создать так A U (B X A), что эквивалентно хаскельному:
data Type1 A B = A | Type1Cons B A
точно такой же тип можно и теми же операциями (U и X) можно создать в лиспе. Далее, используя эти две операции можно создать сколь угодно сложный составной тип из набора ранее определенных типов. ЭТО АЗБУКА. И это можно сделать в любом языке!!!!, а не только в Хаскеле.
Вообще, с точки зрения лямбда исчисления данные - это то, что определено конструкторами, селекторами и условиями на них, поэтому на этом уровне абстракции разницы между данными и процедурами, порождающими эти данные нет. Замечательный пример из SICP про реализацию лиспового cons:
(define (cons x y) (lambda (m) (m x y)))
(define (car z) (z (lambda (p q) p)))
Отличие здесь в том, что в хаскеле есть для этого специальный синтаксис в виде data и отличное пространство имен, а в лиспе нет, хотя по сути, из моего примера с Type1, "Type1Cons" - это конструктор ничем не отличающийся от cons. И самое главное, если в лиспе нет специального синтаксиса для чего-то, то это не значит, что его нельзя создать (не путать, например, с упомянутым ранее монадическим '>=' - это всего лишь инфиксная форма функции) - лисп в отличие от хаскеля здесь просто выигрывает, и тогда весь синтаксический сахар (а data это именно он, т.к. по сути это специальная форма для определения лямбда-абстракций) хаскеля ни чего не доказывает. Лисп - расширяемый язык, хаскель - нет.
ЗЫ про Template Haskell я в курсе, но это уже как раз набор нестандартных костылей в отличие от лиспа :)))
>Далее, используя эти две операции можно создать сколь угодно сложный составной тип из набора ранее определенных типов.
Ключевое слово _ранее определенных_. Опять же это хорошо когда просто типы, и другое когда типы с параметрами. Опять же в определении
data Maybe a = Just a | Nothing
Никакой из упомянутых типов не был определен ранее.
>Лисп - расширяемый язык, хаскель - нет.
Ага - скажи это авторам расширений для GHC... Пользовательские операторы и типы без всяких Template Haskell'ев определяются - чаще всего этого достаточно. Просто Лисп сам примитивен и имеет мало синтаксического сахара, поэтому есть макросы для добавления "сахара по вкусу". В хаскелле же наоборот дофига сахара (причем хорошего, универсального и хорошо ложащегося на парадигму) поэтому желание добавить что то возникает реже.
Подведя итог - Лисп более универсален, но для применения в какой то области требует заточки обычно в виде превращения его в DSL. А haskell уже DSL для ФП со всеми плюсами и минусами, поэтому для задач хорошо ложащихся на ФП Хаскелл круче лиспа, так же как на лиспе проще в не ФП стиле писать...
В Haskell очень красиво выглядят фундаментальные алгоритмы. Вот например КМП:
data KMP a = KMP { kmpDone :: Bool, kmpNext :: (a -> KMP a) }
isSubstringOf :: (Eq a) => [a] -> [a] -> Bool
isSubstringOf w s = match (makeTable w) s
where match table [] = kmpDone table
match table (x:xs) = kmpDone table || match (kmpNext table x) xs
makeTable :: (Eq a) => [a] -> KMP a
makeTable xs = table
where table = makeTable' xs (const table)
makeTable' [] failure = KMP True failure
makeTable' (x:xs) failure = KMP False test
where test c = if c == x then success else failure c
success = makeTable' xs (kmpNext (failure x))
> С чего многоувожаемый ЛОР аналитик сделал такие далеко идущие выводы? Во первых Haskell куда более точно отражает ФП парадигму, а это дает ему право заставлять приспосабливатся к нему
К кому? Вот именно, не все задачи решаются в функциональном стиле (просто и эффективно), хаскель же предлагает использовать для решения этих задач костыли. А приспосабливаться он не может к окружающей среде. которая требует и функциональные и императивные инструменты. Основное же достоинство лиспа, как говорил Брюс Ли:
`Будь водой, если воду налить в стакан -- она примет форму стакана, если налить в кувшин -- она примет форму кувшина...'
А хаскель... он не умрет конечно же, просто он останется академическим языком, в котором будут обкатывать новые фишки для других языков.
>> Ключевое слово _ранее определенных_. Опять же это хорошо когда просто типы, и другое когда типы с параметрами. Опять же в определении
>> data Maybe a = Just a | Nothing
>> Никакой из упомянутых типов не был определен ранее.
Just a и Nothing - это не типы а КОНСТРУТКОРЫ!!!! Тип здесь Maybe a. Ты внимательно читал, что я написал? Здесь ты создаешь при помощи конструкторов тип Maybe a из a. Тип a уже определен ранее - это любой наперед заданный тип или, что тоже самое, любой определенный ранее или позднее (полиморфность, мать ее:)). Кроме этого следует разделять описание типа и его реализацию. Когда ты пишешь data = ... у тебя есть только описание типа, но нет данных. Когда ты напишешь функцию, принимающую "a" и возвращающую "Maybe a" (т.е. _реализуешь_ конструктор) ты реализуешь тип. Для лиспа описание типов данных не требуется в нем описание и реализация "склеены воедино". Для хаскеля описание типов тоже не требуется, кстати. Ты можешь работать с деревьями и списками не имея описания в виде data :)))
>> Пользовательские операторы и типы без всяких Template Haskell'ев определяются - чаще всего этого достаточно.
Пользовательский оператор - это специальная форма для функции. При том синтаксис всегда один и тот же, например для +: a + b или (+) a b. Вот что там стоит вместо плюс ты можешь напрограммить сам, а вот изменить синтаксис выражения ты не можешь, тут и вылазит мощь лиспа :))). Ну а создание типов данных есть во всех языках даже в C :)))
>> Подведя итог - Лисп более универсален, но для применения в какой то области требует заточки обычно в виде превращения его в DSL. А haskell уже DSL для ФП со всеми плюсами и минусами, поэтому для задач хорошо ложащихся на ФП Хаскелл круче лиспа, так же как на лиспе проще в не ФП стиле писать...
DSL обычно описывает проблемную область, а не парадигму. В DSL ты оперируешь понятиями из реального мира, а не функциями и списками. Типа такого:
Он пока не перешел даже в разряд малоиспользуемых языков, как Lisp, например, какой уж тут мейнстрим...
> но планы по усилению императивных возможностей имеются, так что глядишь, когда-нибудь созреем ;)
Боюсь, если добавлять императивные возможности, он превратиться в сборище костылей и подпорок и вся его `красота и чистота' (мне не нравиться его синтаксис) улетучиться.
> К кому? Вот именно, не все задачи решаются в функциональном стиле (просто и эффективно), хаскель же предлагает использовать для решения этих задач костыли. А приспосабливаться он не может к окружающей среде. которая требует и функциональные и императивные инструменты.
Но отказываясь от чистоты языка ты во многом теряешь плюсы ФП, как то - ленивость, прозрачность по ссылкам, усложняется распаралелливание и оптимизация. Поэтому императивщину стоит отделять от функциональщины, что в Хаскелле и сделано.
Вот я слышал что Erlang -- Concurrent Jedi, а вот чтобы Haskell со своими убогими компиляторами/интерпритаторами хватал звезды... это что-то из области фантастики. Все эти мегавозможности, большей частью, красивы и мощны только на словах и учебных примерах, а вот как до реальных проектов дело доходит, так сразу появляются костылики. А функционалность -- это конечно хорошо, но хорошо в меру и серебряной пулей не является. Water-like Lisp -- наше все :)
> Для хаскеля описание типов тоже не требуется, кстати. Ты можешь работать с деревьями и списками не имея описания в виде data :)))
Думаешь я этого не знал?
>Вот что там стоит вместо плюс ты можешь напрограммить сам, а вот изменить синтаксис выражения ты не можешь, тут и вылазит мощь лиспа :)))
Ну почему, я могу определить префиксный, инфиксный и постфиксный оператор. А еще есть (.) и flip чтобы эффективно писать. А другие извращения с синтаксисом которые вы у себя в Лиспе вытворяете - суть есть изврат и есть более прямые способы добиваться того же...
Зависит от твоего опыта. Я, например, до изучения фп работал в основном со статически типизированными языками, поэтому мне проще было начать с Caml-а и Haskell-а.
>> просто у вашего оппонента видимо нет понимания того, за что он борется :)
Все через это проходят. Сначала долго выбираешь самый крутой язык, пытаешься доказать всем и себе, что он действительно крут. А потом все становится пох :)))
>Все через это проходят. Сначала долго выбираешь самый крутой язык, пытаешься доказать всем и себе, что он действительно крут. А потом все становится пох :)))
+1
Потроллить все равно весело, и всегда найдется новый язык, который в чем то круче... :)
> Приведи осмысленный пример задачи и как она упрощается введением своего синтаксиса в Лиспе...
Специальные языки для format и loop. Ладно с format'ом, но loop - это добавленный синтаксис. Заметь, у loop синтаксис не-lispish, и это как нельзя лучше доказывает, что в Лисп можно встроить практически любой язык.
>Специальные языки для format и loop. Ладно с format'ом, но loop - это добавленный синтаксис. Заметь, у loop синтаксис не-lispish, и это как нельзя лучше доказывает, что в Лисп можно встроить практически любой язык.
Да можно такое через Template Haskell замутить, но смысла особого нет... Хаскелл не Лисп и смысля его в Лисп превращать нет никакого...
>> curry :: ((a, b) -> c) -> a -> b -> c
>> curry f x y = f (x, y)
А если параметров, скажем 5 или 7 штук? Или вообще неизвестно сколько их будет, как тогда? :))
Про curry с разными параметрами - по аналогии. А количество параметров всегда известно в Хаскелле. Для произвольного числа параметров искаропки нельзя, потому как нельзя в таком случае типы проверить...
> Ну если в хаскеле не нужны некаррированные функции, зачем тогда там есть uncurry ?
Сам не знаю :) Оно "преобразует" каррированную функцию в функцию из двух аргументов в функцию получающую на вход один агругмент - тьюпл. Все равно что в Си
int foo(int a, int b) => int foo (struct { int a; int b; } c)