Pull to refresh

Comments 107

Функциональщина — это всегда плюс.

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

Люблю ФЯ за их красоту и лаконичность. Не за производительность и «другой подход» к решению задач. Нет, именно за синтаксис. А данная библиотека — неисчерпаемый источник этого счастья. Большинство функций простые, короткие и понятные.

Спасибо, что еще раз напомнили.
Очистим же разум от императивной скверны! (Чую минусов отхвачу, но не могу удержаться)
Вы правы. Синтаксис — одно из основных преимуществ ФЯ.
С.М.Абрамов, о котором я писал в статье (еще раз большое ему спасибо!), рассказывал о своем первом знакомстве с Haskell. Он увидел, как, в какой-то статье про алгоритмы, функции записывались в очень интересном виде. Вроде бы без специальных типографских символов для формул, но все понятно. И он переписал некоторые свои статьи согласно этому синтаксису. И в каком же он был шоке, когда, некоторое время спустя, ему сказали, что он запрограммировал свои статьи на Haskell. А потом, он запустил это все в интерпретаторе и оно заработало.
Вот уравнение

image

solve (fvm::ddt(U) + fvm::div(phi, U) + fvm::laplacian(nu, U) == -fvc::grad(p) + f);

А вот это код на С++, решающий это уравнение в частных производных (естественно, не без библиотек). Так что какой-то особый синтаксис языка необязателен, чтобы вычисления на нём можно было писать интуитивно, особенно если язык — такой монстрик, как С++.
Если бы окружающий мир был свободен от императивной скверны, все было бы гораздо проще.
Не хотелось бы разводить холивар ФЯ vs ИЯ. Думаю, каждый язык имеет свои преимущества, недостатки и область применения.
Тоже люблю ФП, но вот не надо экстремизма. :)
Я больше про несовместимость pure & lazy с реальным миром.
Ааа, понял. Я то думал что наезд на ООП.

Вообще, ООП берет за основу «идеологии» реальный мир. Pure ФП использует математическую модель вселенной. А компьютеры, как мне кажется, работают в своей третьей вселенной, абсолютно отличной от первых двух. Но мы, примитивные существа, не в состоянии осознать её правила, так как приучены мыслить в рамках объектов и процессов. Вот и напридумывали «переводчиков» разной степени кривости…

Это так, минутка философии в комментариях на хабре… :)
Только загвоздка в том, что программы пишутся не для вселенских, а для людских задач — задач «примитивных существ». Соответственно, математические задачи решаются на одних языках, манипуляция объектами осуществляется с помощью других языков, парсеры-лексеры описываются ещё своими языками.

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

Во-вторых, программы не пишутся для людских задач. Программы пишутся для замены одних байтов на другие. Веб-сайт — это не страница (как в книге). HTTP — это не кабель. gif — это не рисунок. Единственные задачи которые пишутся для реальных «людских» задач — это моделирование физических (и не только) процессов. И там, как ни странно, ООП редко используется.
Вы немного отстали от жизни. Люди больше не читают книг — люди большей частью сидят на веб-сайтах. Прокладывают кабеля люди тоже достаточно редко. И картины с рисунками уступили своё место всяким джипегам. Нравится вам это, не нравится вам это, но Интернет и компьютеры (а также внутренние «несуществующие» объекты наподобие файлов) являются частью реального мира. Ещё раз — файлы являются объектами реального мира. Деление на Виртуальность и Реальность с недавних пор стало бредом.

Никакому человеку не нужно заменять одни байты на другие. Людям нужно смотреть картинки, читать посты, писать сообщения, слушать музыку и смотреть видео. Это всё — людские задачи. То, что вы их называете «заменой одних байтов на другие», имеет смысла ничуть не больше, чем описание ваших повседневных задач как «перемещение атомов с одного места на другое».

А вот как раз моделирование физических и не только процессов нужно очень ограниченному кругу людей. И я был бы весьма удивлён, если бы там использовалось ООП. Моделирование — это математическая задача.

А вот провести пальцем по гладкой пластинке, что повлёчёт за собой передачу определённых сигналов, которые заставят измениться две переменные типа integer, что приведёт к тому, что определённым образом изменится поляризация жидкокристаллических ячеек — и последующее изменение давления пальца на пластинку, что вызовет подачу сигналов в два устройства, которые начнут вибрировать — это людская задача. «Включить песню» называется.
«изменить две переменные типа integer» — это и есть «заменить одни байты на другие». Почему первое — людская задача, а второе — нет, непонятно. Подача сигнала на устройство также выполняется как замена байтов на порте устройства.
Именно это я и хотел сказать. Задачи «зайти на сайт», «послать сигналы» и «изменить байты» эквивалентны — и, следовательно, все являются людскими.
Но сказали-то что замена байтов не имеет смысла.
Но задача «зайти на сайт» очевидно является людской.

Любую задачу можно разложить на мелкие подзадачи вроде передвижения атомов, замены байтов или выезжание объекта из абстрактной фабрики, а потом сказать «ага, вот эти все задачи по отдельности не людские, значит и вся целая задача тоже не людская!»
Вот только спора об определениях нам не хватало. Я уверен, что я реален не меньше, чем окружающие меня предметы и явления.

Если вы намекаете на то, что всё на самом деле «нереально», то хотелось бы заметить, что в контексте этой дискуссии понятие реальности не связано с соответствующим понятием в философии. Я уже давно замечаю, что понятия «виртуального» и «реального» мира в компьютерных дискуссиях (простое желание гиков самоидентифицироваться и отделить себя от «некомпьютерных» обывателей) начинают путать с понятием виртуальной реальности, известной задо-олго до возникновения Интернета.
> Я уверен, что я реален

Так, просто к слову: А кем уверен? Кто Вас в этом уверил? :)
Мне просто нравятся игры слов…
Вы не совсем правы, практически всё современное ПО для моделирования физики как раз ООП (может конечно в разных областях по-разному). Во всяком случае, то с чем я знаком.
Ок, спустимся от философии к практике. ФП-язык и ООП-язык — не говоря уже о DSL парсеров — строятся на различных наборах понятий. И ничего из этих понятий не существует для процессора. Для процессоров не существует типов, объектов, функций, даже циклов. Даже присваивания как такового не существует, потому что нечему присваивать. Процессоры абсолютно императивен, если вспоминать про декларативные языки.
Вот вам и разные вселенные. И для преодоления разрыва между этим вселенными используются бесконечные костыли разных уровней — vtbl, thunk-и, кодогенерация, виртуальные машины, монады и прочие многообразные ужасы. И что самое печальное — для того, чтобы что-то написать для своих людских задач, используя приятную тебе семантику, нужно преодолеть этот разрыв и не забыть про abstraction penalty этого преодоления.

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

Всё намешали в одну кучу.

Виртуальные машины вроде LLVM используются не для преодоления какого-то мифического разрыва между вселенными, а для упрощения кодогенерации на разных (но схожих) платформах. Вы бы ещё сказали, что язык Си используется для преодоления разрыва. Процессор, ассемблер, Си, промежуточный язык GHC, сам Хаскелл — это что, всё разные вселенные? Раздули из мухи (ну ладно, мышки или хомячка) слона.

Монады вообще никакого разрыва не преодолевают. Всего лишь удобная абстракция. В ранних версиях Хаскелла обходились и без них.

Вообще, у вас какой-то очень странный взгляд на абстракции — вы их все назвали «бесконечными костылями». Ну да, костыли — если считать проблемой то, что у нас не один утверждённый Богом язык, компилятор, процессор, алгоритм, структура данных и т.д., а много. По-моему, это как раз не проблема.

И что самое печальное — для того, чтобы что-то написать для своих людских задач, используя приятную тебе семантику, нужно преодолеть этот разрыв и не забыть про abstraction penalty этого преодоления.

Вспомните — несколько десятков лет назад были специализированные Лисп-машины. А сейчас их нет. И что? Если современного Лиспера пересадить на Лисп-машину вместо компьютера с «абсолютно императивным» процессором, для него ничего не изменится. Ну разве что работать быстрее станет. Наверное.

Я, кстати, не понимаю, как вообще можно программировать, не помня об этом.

А я понимаю. Не так страшен abstraction penalty, как его малюют.
Компьютеры как раз работают в реальной вселенной (и являются ее частью), так же как и человеческий мозг. Отсюда и проблема — чтобы решить проблему расово правильно, надо сделать две межвселенских перехода: мозг-математика и математика-компьютер, в то время как рядом соблазнительный прямой туннель мозг-компьютер — но который с точки зрения математики оказывается кривым. Такая вот печалька.
математика — она везде «причем». А там, где не причем — значит, эта область знаний/практик недоразвита.
Я захожу на сайт и как будто бы не использую никакой математики. Я читаю книгу с экрана и тоже математики не применяю, потому что книга про поросёнка Петю и к математике не относится. Вот он, прямой туннель мозг—компьютер. Где я должен был совершить два межвселенских перехода, с какой целью, и что не так сейчас?
Вы — не должны, это программист должен, чтобы вас качественно обслужить.
Ага. А программисты бывают разных уровней. На самом нижнем уровне (структура процессора) нужны физика и математика. Из этого не следует, что математика также нужна и на всех высших уровнях. Или вы все абстрации заносите в раздел «математика»?
Нет, не все. Но математики всегда чувствуют, является ли та или иная абстракция математической, и отторгают нематематические. Иногда перебарщивают и отвергают то, что впоследствии признают (например, операционное исчисление).
Я бы очень поостерёгся делить абстракции на математические и нематематические. Файл — математическая абстракция или нет?
Конечно, остерегайтесь, чтобы не попасть впросак в глазах математиков.

Думаю, что математики не признАют файл математической абстракцией. Впрочем, я не математик.
Нет, я остерегаюсь по той причине, что делить мир на чёрное и белое в большинстве случаев неверно.

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

Ко многим повседневным задачам файлы подходят хорошо.
И, снова, о важности прелюдии в процессе любви! (на этот раз к хаскелю)
Особенно по сранению с такими прекрасными и удивительными языками как J ;)
Прелюдия (Prelude) — стандартная библиотека самых необходимых функций, определяется стандартом Haskell 98.

Хоть это, безусловно, и правда, но хочу заметить, что Haskell 98 уже устарел. Текущая версия стандарта — Haskell 2010.
Изучение стандартных библиотек и различных исходников — основа основ. Честно, ожидал увидеть некое сравнение с империческими ЯП, показывающее, как лаконично можно решать большие и объемные задачи.
А в итоге выдержки из прелюдии и фраза, которая повторяется чуть ли не через каждое слово — «читайте прелюдию».
Это всё равно, что выкладывать куски кода из msdn и говорить: «Ой, как красиво написали!»
Никого не хотел обидеть. Просто впечатление от прочтения.
Если бы автор начал сравнивать хаскель с императивными языками — начался бы срач (так всегда бывает).
На хабре статья есть «через тернии к haskell» там есть сравнения. Прсмотрите.
О, спасибо, начало статьи интригующее: добавил в закладки для вечернего чтения )
К сожалению, карма не даёт плюсануть.
Да, никто не обижается. Сам вижу, что получилось не то, что хотел. Понимаете, эмоции переполняют, а, вот, излагать материал плохо получается. Надеюсь, только, что кого-нибудь этот текст подтолкнет к изучению Haskell.
Я когда впервые увидел определение Eq тоже был счастлив. =)
Если бы вы ещё описали, что именно и как эти функции делают, было бы совсем замечательно.
Вы ведь рекламируете Хаскель тем, кто с ним никогда не работал? Но человеку, который с Хаскелем незнаком, ваши восторги непонятны.
PS
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++ .>+.+++++++..+++.>++.<<+++++++++++++++.>.+++. ------.--------.>+.>.
Правда, головокружительная красота?
Помогу немного автору:

head — вернуть первый элемент list'a.
tail — вернуть list без первого элемена.
x:xs — добавить элемент x в начало list'a xs и вернуть этот list
[] — пустой list (как и у всех языках, в принципе)

Надеюсь стало немного понятнее.
Вы как раз описали наиболее понятные конструкции.
Вот эти гораздо интереснее:
:: ->
И, наконец, что это:
(_:_)
tail :: [a] -> [a]
Функция tail принимает на вход список элементов множества а и возвращает список элементов множества а.
xxx :: [Integer] -> Bool -> [Bool]
Функция xxx принимает на вход список элементов Integer и значение Boolean, а возвращает список Boolean.
(_:_) — непустой список.

А, ок, виноват:

После :: идет описание типа функции/данных.

-> используется в описании типов для обозначения обработки данных (transformation). Если по сути, слева от этого знака — тип аргумента функции, а справа — тип возвращаемого значения. Например Int -> Boot — тип функции которая принимает целое число и возвращает булевское значение.
Пример посложнее: Int -> Float -> Bool это функция, которая принимает аргумент Int и возвращает функцию Float -> Bool. Благодаря синтаксическому сахару первой функции можно передать 2 значения (Int и Float) и рассматривать ее как функцию с двумя аргументами, которая возвращает просто Bool.

(_:_) — здесь все просто: скобки для компилятора — можно игнорировать. Подчеркивание _ значит «любое значение». Соответственно _:_ значит «head list'a — любой елемент, tail list'a — любой список». Под это определение подойдет любой список за исключением пустого, так как он не удовлетворяет первую часть условия (head list'a — любой элемент), так как «head []» — это не валидная конструкция (в рантайме вызывает эксепшн).
В коротком комментарии, пожалуй, не объяснишь, что такое декларация типа (::), стрелка (->), образец вообще и список вида голова+хвост ((_:_)) в частности, но, чтобы поддержать Ваш интерес к Хаскелю, скажу, что даже такая конструкция имеет смысл, значение и тип:
(.)(.)

Сломал себе мозг думая где это может быть использовано и к чему оно приведет в последствии. :)
Prelude> :t ((.)(.))
((.)(.)) :: (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c

Prelude> let f = ((.)(.)) (:) (+ 1) (const [])
Prelude> :i f
f :: a -> [Integer -> Integer] — Defined at :45:5
Prelude>

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

Поигрался в ghci — понял. Мне кажется если добавить еще одну сись композицию — можно понять вселенную где-то до девятого измерения.
((_:_))

((.)(.))


Это серьезно? А вот такое есть?

((_._))


Нет, к сожалению. Сделать-то можно, но это будет моветон.
я думаю, это можно добавить в статью )
Синтаксис приведенных функций аналагичен обычной математической записи. Но, Вы, правы. Буду тренироваться. Если написал никому не нужный текст — простите, готов удалить.
Кстати, я вот тут собирался написать статью (или даже несколько) с подробным описанием типизации данных в Haskell. Очень уж это широкая и элегантная концепция. Как думаете, есть смысл писать о типах данных (и их обработке на этапе компиляции), в отрыве (насколько это возможно) от выполнения кода (рантайма)?
Думаю, стоит.
Тема несколько необычная и у меня лично вызвала самые большие затруднения при изучении хаскеля. После императивных яп трудно воспринимается. Если в статье подход к обяснениям будет не такой, как в большинмстве учебников — обязательно прочитаю. Взглянуть с другой стороны, так сказать.
С нетерпением буду ждать :-)
Попытаюсь что-то «скомпилировать», тем более что уже есть некоторые наработки. Спасибо.
К сожалению не могу того же сказать про Scala. Открываешь исходники — а там всякие оптимизации, иногда с вкраплениями императивщины. Так что, еще один аргумент в пользу чисто функциональных языков — чистота библиотек.
UFO just landed and posted this here
UFO just landed and posted this here
А о чем это вы? Не дадите ссылку?
UFO just landed and posted this here
Какой то он подозрительно сиподобный (насколько это возможно в функциональных языках)… Нет, все же Хаскел красивее.

Но туториал прочту.
UFO just landed and posted this here
UFO just landed and posted this here
Понял всё, кроме того, что такое класс Eq и что он делает.
Это тип данных, которые можно сравнивать (перегружают операции сравнения).
В общем, если я правильно понял, это абстрактный класс, операции == и /= сами ничего не делают, и одну из них надо в производном классе перегрузить.
В Хаскеле класс, а точнее класс типов — это не то же самое, что в императивных языках. Это больше похоже на тип данных.

Я очень плохо умею что-нибудь объяснять, поэтому лучше посмотрите сами, что бы я вас не запутал:
learnyouahaskell.com/types-and-typeclasses#typeclasses-101
learnyouahaskell.com/making-our-own-types-and-typeclasses#typeclasses-102
Правильно-ли я понимаю, что length — это рекурсия? Как и map.
Да, но length оптимизируется в цикл.
А map в цикл не оптимизируется, но ему и не надо. Если интересно, могу объяснить, почему.
можете объяснить, почему не оптимизируется, или почему ему и не надо?
параллелить можно
потому что Хаскель справляется с бесконечными списками
Потому что ленивость. Представьте, что вместо списка у вас Питоновский генератор — применение функции к каждому элементу не должно никак влиять на производительность до тех пор, пока вы эти элементы не начнёте вычислять. А вот для массивов да векторов map отлично оптимизируется в цикл.
Делал несколько подходов к Haskell. Спору нет, после него все остальное выглядит блекло. Думаю человек, которому удалось сделать на нем что нибудь полезное, что нибудь нужное в быту, никогда уже не сможет вернуться к другим языкам. Мне не удалось. А сколько разочарований: неизбежность компиляции по месту потребления, громоздкость monad transformers, вечная борьба с cabal, ощущение ненужности на freenode.net #haskell — нет я могу понять о чем они говорят, не постигаю зачем. И в конце болезненное возвращение к серости земного. Для себя решил, пусть любовь к Haskell остается возвышенной и нереализованной.
Может изучение Haskell и делает программиста лучше, но по моему несчастней.
Неужели, существуют люди, которым это не нравится?


Существуют. (_:_) — это невозможно понять — это нужно просто запомнить :) Почему-то большинство функциональных языков очень любят использовать спецсимволы для основных, я бы сказал фундаментальных, конструкций. Ладно скобочки но сложно вместо -> написать function например. Мне нравятся принципы ФП изучать, но чисто теоретически — читать ФЯП их не могу, тем более на них писать. Разве что в PHP использовать принципы :)
(_:_) — это невозможно понять — это нужно просто запомнить :

Ну прямо таки!

Две абстракции:
"_" — означает — тут стоит параметр, но нам не нужно её значение, нам пофиг на него, и оно вычислено не будет (по крайней мере для ленивых значений)

":" — это соединитель хвоста и головы в список.
В терминах самого Хаскеля, список определён так:
data [a] = [] | a : [a]

По-русски — список — гомогенный — он пустой([]) или ( там стоит ИЛИ | ) состоит из головы и списка. Это рекурсивное определение.

Исходя из этого, (_:_) — это непустой список, где нам неважны его значения.
ЕМНИП, оно может вообще быть никогда не вычислено, даже если там не _, а что-то другое. Т. е. в данном случае мы просто пишем это, потому что не интересуемся значением и чтобы не вводить новую переменную в локальной области видимости. Если бы мы написали (eval:l), то eval, в отличие от l, не вычислилось бы, потому что нигде не используется в локальной области видимости.
"_" — означает — тут стоит параметр, но нам не нужно её значение, нам пофиг на него, и оно вычислено не будет


Оно не будет вычислено, даже буде там x написан. Список будет приведён к WHNF, т.е. вычислит, что он не пустой, а с головой и хвостом, но не вычислит ни саму голову, ни хвост:

test = case (undefined:undefined) of
  [] -> False
  (x:y) -> True


Результат: True
В Haskell все функции ровно одного аргумента, для реализции функции многих аргументов применяется концепция карирования, то есть применения аргументов к функции по очереди. Результат применения первого аргумента суть функция остальных. В такой семантике «вместо -> написать function» не получится.
"->" — это еще ничего. В Racket, который тесно связан со своей IDE — есть даже греческая буква λ для лямбда-выражений. Хорошо хоть ключевое слово lambda оставить додумались.
Без обид, но если вы считаете что (_:_) это не логично, а function то же самое что и -> вам все же нужно подучить базовые принципы ФП…
Я понятия не имею что значит -> в Хаскеле и поєтому написал «например». Ещё раз — я изучаю теорию, не вдаваясь синтаксис какого-то конкретного ФЯП. Примеры в книгах и статьях обычно есть на каком-то конкретном, но я в них читаю только то, что они иллюстрируют, не обращая внимания на синтаксис, не пытаюсь, например, расшифровать
null             :: [a] -> Bool
, а смотрю на
null []          =  True
null (_:_)       =  False
, не пытаясь расшифровать и (_:_)
(_:_) — это невозможно понять — это нужно просто запомнить

Если вы утверждаете, что что-то невозможно понять, это обычно предполагает, что вы хотя бы попытались это понять.
:) — означает шутку, а не символы какого-то ЯП. Да и сама фраза, как говорится, «мем» из анекдота…
Кроме шутки, у смайлика есть ещё дюжина значений. С учётом того, что в этом же сообщении было
но сложно вместо -> написать function например.

я не воспринял это как шутку.

Вот сейчас хотел привести пример какого-нибудь языка, в котором :) являлось бы самодостаточным осмысленным выражением, но ничего не вспомнил. Никто не подскажет?..
Скобки, как правило, заняты для другого.
(_:_) — это невозможно понять — это нужно просто запомнить

не пытаясь расшифровать и (_:_)


Нда…
В текстах по математике не пишут function, в них пишут, например, f: R->R. Об этом и хаскель.
Ну вы же не пишете plus вместо + и equal вместо ==. Главное один раз привыкнуть.
Это общепринятые обозначения и, да, иногда пишу (PHP не поддерживает перегрузку операторов).
Общепринятость — это очень и очень хорошо. Главное, чтобы это не переходило в консерватизм, который уже так себе.
Не стоило, на мой взгляд, так сходу говорить о классе Eq, предварительно не объяснив что такое классы типов. Новичок совсем ничего не поймёт, а остальные уже успели оценить эстетическую составляющую. В целом, конечно, идея статьи правильная, но напрашивается более развернутое описание.
Если вы знакомы с С. М. Абрамовым, то в своих будущих статьях обязательно вставляйте его цитаты, этот человек — кладезь коротких но емких и умных фраз.
С недавних пор изучаю реализации транзакционной памяти. Очень понравилась реализация Haskell. Предельно простая и понятная, благодаря системе типов языка. Гугление привело на довольно интересные курсы School of Haskell, где многие особенности языка тоже очень доходчиво описаны. До этого лишь немного читал Learn You a Haskell for Great Good, но смог разобраться в необходимой фиче. Рекомендую!
>А как в прелюдии определен класс Eq? Вы будете смеяться!

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

Это всё красиво конечно, но только как оно работает? как реально сравнение происходит? когда 2 функции (== и \=) по сути ссылаются друг на дружку?

Haskell мне интересен — но из за вот таких вот головоломок читать код становится очень тяжело. (хотя наверное это просто из за плохого понимания языка)
Как минимум одну из функций (==) или (\=) переопределяют в instance-ах. И снимают зацикленность.
Как правило, переопределяют (==)
Sign up to leave a comment.

Articles