LINUX.ORG.RU

«О Haskell по-человечески»

 , ,


7

5

http://ohaskell.ru/
Уже было?

Почему эта книга появилась

Меня достало. Почти все книги по Haskell начинаются с демонстрации быстрой сортировки, и ещё что-то там про факториал… Эта книга не такая. Я расскажу о Haskell человеческим языком, с минимумом академизма, с действительно понятными примерами и по возможности лаконично.

Зачем

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

★★★★★

Последнее исправление: cetjs2 (всего исправлений: 2)

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

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

Ага, намекает на то, что там HKT нет.

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

HKT-полиморфизма, прошу прощения

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

Можно сказать «List это аппликативный функтор» или «List это монада», но никак не "; - это аппликативный функтор".

Подразумевается, что семантика точки с запятой содержится в семантике аппликативного функтора. Монада тут не нужна.

Ты хотел сказать, что это (*>) :: f a -> f b -> f b (из Control.Applicative, для некоторого аппликативного функтора f :: * -> *)? Ещё раз:

Нет, я хотел сказать, что аппликативный функтор позволяет «поднять» любую последовательность вычислений. Именно это подразумевается под «перегрузкой точки с запятой». Монада же тут не нужна - смысл монады в операции join, которая позволяет нам опускаться вниз.

иначе бы в хаскелях и скалах скакали вокруг аппликативных функторов, а не монад

Нет.

Да, что характерно — ; заменим (>>=), то есть монадической операцией, а не аппликативной

Но на практике это не так. На практике мы сперва каждую строчку заменяем хитро-вывернутой лямбдой, а потом эти лямбды уже соединяем при помощи >>=. И именно из-за начально преобразования в лямбды нам приходится использовать join. С аппликативным функтором мы можем поднять любое вычисление «напрямую», то есть посчитать 2+2=4, как нормальные люди, не делая лишнего возведения в квадрат и извлечения корня, как в случае с монадой.

примерно «как нам в чистом языке запилить императивщину? С помощью монад, то есть (>>=), то есть программируемого ;»

Нет, это не так. Просто словесная эквилибристика.

Мандады на макаросах и борщесиле, ага.

Нет, ты не понял - это монады являются костыльной заменой макросов, не наоборот. Загоняем нечто под монадический интерфейс и используем до-нотацию в качестве дсля. Монады тут нужны только в качестве интерфейса, который достаточно общий, чтобы можно было под него загнать побольше всего. Если же у нас есть возможность делать специализированные дсли самим, то и монады сразу не нужны - незачем преобразовывать интерфейс к монадическому, можно сразу писать дсл для интерфейса, который соответствует предметной области :)

Становится, по определению.

Но у тебя нету определения. В том и проблема. У тебя есть определение математической монады, но в программировании оно не является корректным и применимым. В программировании объекты определяются по семантике, а не по формальным признакам, такие дела. Смотри выше про обсерверы.

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

А примеры из скалы вообще приводить не надо, т.к. там есть монады, которые нарушают монадические законы. Намеренно, причем :)

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

flatten и return для списков свободно пишется - вот тебе и монада списков. Формально, понятное дело, реально монад нету ни в каком ЯП, потому что их не существует вообще.

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

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

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

Действительно, лучше пойти пиво с «поцонами на раёне» попить.

Луче пойти и заняться чем-нибудь интересным. Или полезным. Или поработать. Или отдохнуть. Некоторые даже умудряются делать это всё одновременно. Но вот справочники никто в здравом уме не читает «от корки до корки».

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

Подразумевается, что семантика точки с запятой содержится в семантике аппликативного функтора

Ты расскажи — что за функтор, на какой категории и т.п. С математической строгостью, можешь paper какой-нибудь кинуть.

То есть _;_ это бинарная операция (функция) которая принимает... что?

Вот в хаскеле понятно. Есть IO : Type -> Type — функтор, аппликативный, монада. _;_ то есть (*>) принимает два выражения типов IO a и IO b, даёт в итоге IO b. Так что можно написать что-нибудь вроде print 1 *> print 2, ну то есть та же семантика, что и у print 1 ; print 2. Но этого не достаточно, так как x <- read не является выражением какого-то типа, это синтаксическая хрень, так что в x <- read ; print x _;_ не является какой-то функцией, для перевода её в функцию с сохранением семантики нужна (>>=), то есть read >>= \x -> print x. Непонятно про что ты вообще говоришь — какой-такой ; и «аппликативный функтор».

это монады являются костыльной заменой макросов, не наоборот.

Ну монады вообще не дают того что дают макросы — они вообще про другое. Макросами можно реализовать синтаксическое расширение для do-like-сахара поверх монадических функций (которые легко пишутся в любых языках с ФВП и подобными).

У тебя есть определение математической монады, но в программировании оно не является корректным и применимым.

Да мне как-то всё равно. Есть определение монады, так что любой тип который ему соответствует (и псевдо-монадические типы, без соблюдения аксиом) является монадой (псевдо-монадой), для всех таких работает монадический код (и обладает своими свойствами, в случае настоящих монад). А за конкретную семантику в каждом конкретном случае — это отдельный разговор.

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

Ты расскажи — что за функтор, на какой категории и т.п. С математической строгостью, можешь paper какой-нибудь кинуть.

Да любой. Это смысл аппликативного функтора. Если у тебя есть некоторый код и некоторый аппликативный функтор. Например, если у тебя есть чистый код и аппликативный функтор IO, то можно сделать код погруженный в IO. При чем для чистой точки запятой (если больше никаких связываний нет), достаточно и обычного функтора (то есть fmap).

То есть _;_ это бинарная операция (функция) которая принимает... что?

Обычная композиция :) В смысле совсем обычная.

Непонятно про что ты вообще говоришь — какой-такой ; и «аппликативный функтор».

Я говорю о том, что ; определяется аппликативным функтором, а не монадой. Чтобы переопределить ; надо переопределить соответствующий аппликативный функтор - не монаду.

Ну монады вообще не дают того что дают макросы — они вообще про другое.

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

Макросами можно реализовать синтаксическое расширение для do-like-сахара поверх монадических функций

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

Есть определение монады

Нету.

так что любой тип который ему соответствует

Только вот у нас очень-очень мало ЯП, в которых вообще есть штука, которая удовлетворяет математическому определению «типа» ;)

В частности, ни в одном из мейнстримных ЯП типов нету. В том числе и в статически типизированных, да. Там есть нечто, что мы называем типами, но оно совершенно точно не удовлетворяет определению «типа». Соответсвенно, нету там и монад (и быть не может).

А за конкретную семантику в каждом конкретном случае — это отдельный разговор.

Так исключительно в семантике вся суть. Рассуждать о чем-то не определив семантику чего-то - совершенно бессмысленно и бессодержательно.

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

Что смотреть? вот у тебя етсь return, fmap и bind, напиши мне хоть одну осмысленную ф-ю не однострочник из этого.

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

Еще раз, ты написал:

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

Достаточно одного контр-примера, чтобы было ясно, что ты лжешь.

Итак, открываем Control.Monad:

-- | @'replicateM' n act@ performs the action @n@ times,
-- gathering the results.
replicateM        :: (Monad m) => Int -> m a -> m [a]
replicateM n x    = sequence (replicate n x)

replicateM работает с очевидной семантикой для любой имплементации Monad. QED.

Причем тут твоя «осмысленность» и «однострочники» я не понял, и, если честно, то не особо и интересно.

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

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

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

Достаточно одного контр-примера, чтобы было ясно, что ты лжешь.

Конечно.

Причем тут твоя «осмысленность» и «однострочники»

Как при чем? Бессмысленный и тривиальный код никому не нужен. Напоминаю, разговор был о возможности писать код абстрагированный от конкретной монады. Под «кодом», естественно, могут пониматься только оплезные функции - а значит осмысленные и нетривиальные. Бесполезные ф-и, понятное дело, никому не нужны.

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

Например, если у тебя есть чистый код и аппликативный функтор IO

Хорошо, вот выхлоп GHCi:

Prelude> :m + Control.Applicative
Prelude Control.Applicative> :i IO
...
instance Monad IO -- Defined in `GHC.Base'
instance Functor IO -- Defined in `GHC.Base'
instance Applicative IO -- Defined in `Control.Applicative'
Prelude Control.Applicative> fmap id (print 1)
1
Prelude Control.Applicative> print 1 *> print 2
1
2
Prelude Control.Applicative> print 1 >> print 2
1
2
Prelude Control.Applicative> do { print 1 ; print 2 }
1
2
Prelude Control.Applicative> getLine >>= \line -> print (1 + read line)
1
2
Prelude Control.Applicative> do { line <- getLine ; print (1 + read line) }
1
2
Prelude Control.Applicative> print 1 . print 2

<interactive>:4:1:
    Couldn't match expected type `b0 -> c' with actual type `IO ()'
    Relevant bindings include it :: a -> c (bound at <interactive>:4:1)
    Possible cause: `print' is applied to too many arguments
    In the first argument of `(.)', namely `print 1'
    In the expression: print 1 . print 2

<interactive>:4:11:
    Couldn't match expected type `a -> b0' with actual type `IO ()'
    Relevant bindings include it :: a -> c (bound at <interactive>:4:1)
    Possible cause: `print' is applied to too many arguments
    In the second argument of `(.)', namely `print 2'
    In the expression: print 1 . print 2
Prelude Control.Applicative> :m + Control.Monad
Prelude Control.Applicative Control.Monad> ((const $ print 1) >=> (const $ print 2)) ()
1
2
Prelude Control.Applicative Control.Monad> ((const $ print 1) . (const $ print 2)) ()
1
Prelude Control.Applicative Control.Monad> :m + Control.Arrow
Prelude Control.Applicative Control.Monad Control.Arrow> ((const $ print 1) >>> (const $ print 2)) ()
2

Не понятно — 1) каким боком тут fmap и обычная композиция — или напиши как fmap и/или (.) использовать в качестве _;_, 2) таки для настоящей _;_ которая фигурирует в T x = Con(); use(x) и x <- con; use x нужна монада, то есть (>>=) — или напиши как такое сделать используя только Applicative ((*>), (<*>), pure).

1) Только если вот такое:

> runKleisli ((Kleisli $ const $ print 2) . (Kleisli $ const $ print 1)) ()
1
2
> runKleisli ((Kleisli $ const $ print 1) >>> (Kleisli $ const $ print 2)) ()
1
2

не обычная композиция (другой категории).

Нет, они как раз про то же.

Они не дают произвольных DSL, как макросы. Монадические DSL (типа free monad, например) и синтаксические макросов — разные вещи. Вообще монады это функциональщина, приделывать к ним DSL макросами или делать сахар сразу в синтаксисе языка — совсем другой вопрос.

мы можем свернуть код при помощи do-нотации

А можем не сворачивать — http://www.haskell.org/haskellwiki/Do_notation_considered_harmful.

Нету.

Обычное математическое определение применительно к эндофункторам на категории типов.

В частности, ни в одном из мейнстримных ЯП типов нету. В том числе и в статически типизированных, да. Там есть нечто, что мы называем типами, но оно совершенно точно не удовлетворяет определению «типа». Соответсвенно, нету там и монад (и быть не может).

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.59.8232

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.124.5712

Либо рассматриваем воображаемое подмножество языка в котором «всё хорошо», либо теория семантических доменов с монадами под разные модели вычислений во все поля.ф

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

Давно хотел попробовать этот ваш хаскель. Давно ждал подобную книжечку, надеюсь оправдает ожидания.

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

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

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

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

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

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

Не понятно — 1) каким боком тут fmap и обычная композиция

Мне не понятно что тебе непонятно. ; - это обычная композиция. Ну, берем две функции и получаем из них третью:

f `;` g = g . f
ну да, немного не она - порядок поменять :)

или напиши как fmap и/или (.) использовать в качестве _;_,

Мне непонятен вопрос. У тебя етьс две функции. Действуешь на них композицией (точнее flip .) и получаешь новую функцию, которая и будет результат применения двоеточия. Что тут неясно?

2) таки для настоящей _;_ которая фигурирует в T x = Con(); use(x) и x <- con; use x нужна монада

Нет, не нужна, сколько можно уже повторять?

или напиши как такое сделать используя только Applicative ((*>), (<*>), pure).

Ты понимаешь вообще зачем нужен аппликатив и почему у него именно такой интерфейс, а не какой-либо иной? Именно потому, что это минимальный интерфейс для того, чтобы можно было поднять любой код в функтор. Обычный функтор может поднимать только код вида f (g (h x), аппликативный - произвольный код, т.к. можно каррировать изнутри. При этом f ; g ; h это ни что иное как h . g . f - то есть нам нужен только fmap. bind вообще с этим никак не связан. Ты начитался математических определений, но не понимаешь их смысла.

Обычное математическое определение применительно к эндофункторам на категории типов.

А при чем тут математическое определение? Монада в программировании и монада в математике - это совершенно разные объекты, которые имеют весьма мало общего.

А можем не сворачивать

можем не сворачивать - тогда сворачиваем не в do а в монадические комбинаторы. Это не существенно.

Либо рассматриваем воображаемое подмножество языка в котором «всё хорошо»

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

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

Они не дают произвольных DSL

Ну естественно. Макросы - это полноценное решение, монады - кривой костыль. Но за неимением гербовой хаскиебы пишут на простой :)

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

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

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

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

Что тут неясно?

Рабочий код на хаскеле-то напиши. А то о чём-то своём беседуешь, тогда как print 1 . print 2 ну тупо не компилируется, а ((const $ print 1) . (const $ print 2)) () — тупо не работает.

Композиция будет работать при runKleisli ((Kleisli $ const $ print 2) . (Kleisli $ const $ print 1)) () с (.) из Control.Category для Kleisli которая, внезапно, _Monad_ m => Category (Kleisli m).

Нет, не нужна, сколько можно уже повторять?

Правильно, хватит бередить. Или напиши x <- con ; use x без bind, или пока.

Ты начитался математических определений, но не понимаешь их смысла.

Ты вообще что-то странное пишешь. Вот:

Именно потому, что это минимальный интерфейс для того, чтобы можно было поднять любой код в функтор. Обычный функтор может поднимать только код вида f (g (h x), аппликативный - произвольный код, т.к. можно каррировать изнутри. При этом f ; g ; h это ни что иное как h . g . f - то есть нам нужен только fmap. bind вообще с этим никак не связан.

ЯННП. Будет хорошо, если ты пойдёшь и напишешь (покажешь) компилируемый и работающий File.hs в котором будут показаны «поднятия кода», «каррирование изнутри», увязаны «f (g (h x)», «h . g . f», «fmap», аппликативные функторы и «f ; g ; h» (и x <- f ; g x — обязательно! :)).

Монада в программировании и монада в математике - это совершенно разные объекты, которые имеют весьма мало общего.

Не, монада в программирование это просто частный случай математической монады — в случае категории типов и эндофункторов на ней.

можем не сворачивать - тогда сворачиваем не в do а в монадические комбинаторы.

Я к тому, что вот пишем мы себе обобщённый код с комбинаторами, в т.ч. Control.Monad и проблем не знаем. Никаких do (возможно), DSL-ей и макросов — это у тебя дилеммы с костылями и т.п.

Как ты никрути, а не будет у тебя ни типов, ни монад, в строгом смысле.

Всё там есть, проблема на вашей стороне :)

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

Говорят «Real world haskell» устарела сильно.

В каком месте? Там все в порядке, может быть пару мест и есть(несколько модулей поменяли название), но не существенно.

Waterlaz ★★★★★
()
Ответ на: Согласен с вами от dshevchenko

Читаю.

Автор? Спасибо за книжку, читаю.

Поправьте, пожалуйста, опечатку на стр. 51:

Но если мы заглянем «пот капот» вызова этой функции:

Заглянем мы, конечно же, поД капот.

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

В каком месте? Там все в порядке, может быть пару мест и есть(несколько модулей поменяли название), но не существенно.

Дык, «говорят». Когда сам выбирал, что почитать, то столкнулся с таким мнением. В итоге после нескольких глав отложил. Возможно и не прав.

DarkEld3r ★★★★★
()
Ответ на: Читаю. от anonymous

Стр. 128:

ls | grep xml
... Последовательность даёт нам твёрдую уверенность в том, что до тех пор, пока утилита ls не завершит свою работу, мы не перейдём к утилите grep.

Читать дальше как-то расхотелось. Во-первых, обе утилиты работают одновременно и параллельно, во-вторых, мы никуда не переходим.

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

кружечку кофе

благородные доны лора во всем своем сиянии

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