Какая суета о Хаскелле?

Я знаю нескольких программистов, которые продолжают говорить о Haskell, когда они находятся между собой, и здесь, на SO, все, кажется, любят этот язык. Быть хорошим в Haskell кажется несколько похожей на гениального программиста.

Может кто-нибудь дать несколько примеров Haskell, которые показывают, почему это так элегантно/превосходно?

Ответ 1

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

Другим действительно опрятным в Haskell является его система типов. Он строго типизирован, но механизм вывода типа заставляет его чувствовать себя как программа Python, которая волшебным образом говорит вам, когда вы совершили тупую ошибку, связанную с типом. Сообщения об ошибках Haskell в этом отношении несколько отсутствуют, но по мере того, как вы больше знакомы с языком, который вы скажете себе: это то, что должно быть типичным!

Ответ 2

Это пример, который убедил меня узнать Хаскелла (и мальчик, я рад, что сделал).

-- program to copy a file --
import System.Environment

main = do
         --read command-line arguments
         [file1, file2] <- getArgs

         --copy file contents
         str <- readFile file1
         writeFile file2 str

ОК, это короткая, читаемая программа. В этом смысле это лучше, чем программа на C. Но как это отличается от (скажем) программы Python с очень похожей структурой?

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

Хаскелл "ленив". Он не вычисляет вещи до тех пор, пока это не понадобится, и по расширению не вычисляет вещи, которые ему никогда не нужны. Например, если вы хотите удалить строку writeFile, Haskell не стал бы читать что-либо из файла в первую очередь.

Как это, Haskell понимает, что writeFile зависит от readFile, и поэтому он может оптимизировать этот путь данных.

В то время как результаты зависят от компилятора, что обычно происходит при запуске вышеуказанной программы: программа считывает блок (скажем, 8 КБ) первого файла, затем записывает его во второй файл, затем читает другой блок из первого файла и записывает его во второй файл и т.д. (Попробуйте запустить strace на нем!)

..., который очень похож на эффективную реализацию C-копии файла.

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

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

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

  • Компактный код. Меньше строк для ошибок.

  • Скомпилировать ошибки. Многие ошибки недействительны Haskell.

Хаскелл не для всех. Но каждый должен попробовать.

Ответ 3

Вы как бы задаете неправильный вопрос.

Haskell - это не тот язык, на котором вы смотрите несколько интересных примеров и "ага, я вижу сейчас, вот что делает его хорошим!"

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

  • ленивая оценка
  • Никаких побочных эффектов (все чисто, IO/etc происходит через монады)
  • Невероятно выразительная система статического типа

а также некоторые другие аспекты, которые отличаются от многих основных языков (но разделяются некоторыми):

  • функциональная
  • Значительные пробелы
  • тип выводится

Как и некоторые другие плакаты, комбинация всех этих функций означает, что вы думаете о программировании совершенно по-другому. И поэтому трудно придумать пример (или набор примеров), который адекватно связывает это с Joe-mainstream-programmer. Это экспериментальная вещь. (Чтобы сделать аналогию, я могу показать вам фотографии моей поездки в Японию в 1970 году, но, увидев фотографии, вы все равно не будете знать, каково было жить там в течение этого времени. Точно так же я могу показать вам Haskell "quicksort", но вы все равно не будете знать, что значит быть Haskeller.)

Ответ 4

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

Когда дело доходит до написания программы для использования в реальном мире, вы можете обнаружить, что Haskell отсутствует в какой-то практической манере, но ваше окончательное решение будет лучше для того, чтобы начать использовать Haskell. Я определенно еще не там, но пока обучение Haskell было намного более полезным, чем сказать, Lisp был в колледже.

Ответ 5

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

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

Итак, если вы заботитесь о своей многоядерной работе, Haskell должен что-то сказать. Отличное место для начала - это учебное пособие по параллельному и параллельному программированию в Haskell от Simon Peyton Jones .

Ответ 6

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

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

c1 `mappend` c2 `mappend` c3

где c1 и т.д. return LT, EQ или GT, c1 return EQ заставляет выражение продолжать, оценивая c2; если c2 возвращает LT или GT, что значение целого и c3 не оцениваются. Такие вещи становятся значительно более сложными и сложными в таких вещах, как генераторы монодических сообщений и синтаксические анализаторы, где я могу носить различные типы состояний, иметь разные условия прерывания или, возможно, захотят принять решение по любому конкретному звонку, действительно ли это означает "никакая дальнейшая обработка" или средство "возвращают ошибку в конце, но продолжают обработку для сбора дополнительных сообщений об ошибках".

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

Во всяком случае, в Haskell есть много других хороших вещей, но это очень важный, о котором я не упоминал так часто, вероятно, потому что он довольно сложный.

Ответ 8

Для интересного примера вы можете посмотреть: http://en.literateprograms.org/Quicksort_(Haskell)

Интересно посмотреть на реализацию на разных языках.

Что делает Haskell настолько интересным, как и другие функциональные языки, является тот факт, что вы должны по-разному думать о том, как программировать. Например, вы, как правило, не будете использовать циклы while или while, но будете использовать рекурсию.

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

Ответ 9

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

Например, если вы хотите вычислить все простые числа, вы можете использовать

primes = sieve [2..]
    where sieve (p:xs) = p : sieve [x | x<-xs, x `mod` p /= 0]

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

foo = sum $ takeWhile (<100) primes

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

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

Ответ 10

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

Ответ 11

Я согласен с другими, что увидеть несколько небольших примеров - не лучший способ продемонстрировать Haskell. Но я все равно дам. Здесь быстрое решение для проблемы Эйлера Project 18 и 67, в которых вам предлагается найти путь максимальной суммы от базы до вершины треугольника:

bottomUp :: (Ord a, Num a) => [[a]] -> a
bottomUp = head . bu
  where bu [bottom]     = bottom
        bu (row : base) = merge row $ bu base
        merge [] [_] = []
        merge (x:xs) (y1:y2:ys) = x + max y1 y2 : merge xs (y2:ys)

Вот полная, многоразовая реализация алгоритма BubbleSearch Лэша и Митценмахера. Я использовал его для упаковки больших медиафайлов для архивного хранения на DVD без потерь:

data BubbleResult i o = BubbleResult { bestResult :: o
                                     , result :: o
                                     , leftoverRandoms :: [Double]
                                     }
bubbleSearch :: (Ord result) =>
                ([a] -> result) ->       -- greedy search algorithm
                Double ->                -- probability
                [a] ->                   -- list of items to be searched
                [Double] ->              -- list of random numbers
                [BubbleResult a result]  -- monotone list of results
bubbleSearch search p startOrder rs = bubble startOrder rs
    where bubble order rs = BubbleResult answer answer rs : walk tries
            where answer = search order
                  tries  = perturbations p order rs
                  walk ((order, rs) : rest) =
                      if result > answer then bubble order rs
                      else BubbleResult answer result rs : walk rest
                    where result = search order

perturbations :: Double -> [a] -> [Double] -> [([a], [Double])]
perturbations p xs rs = xr' : perturbations p xs (snd xr')
    where xr' = perturb xs rs
          perturb :: [a] -> [Double] -> ([a], [Double])
          perturb xs rs = shift_all p [] xs rs

shift_all p new' [] rs = (reverse new', rs)
shift_all p new' old rs = shift_one new' old rs (shift_all p)
  where shift_one :: [a] -> [a] -> [Double] -> ([a]->[a]->[Double]->b) -> b
        shift_one new' xs rs k = shift new' [] xs rs
          where shift new' prev' [x] rs = k (x:new') (reverse prev') rs
                shift new' prev' (x:xs) (r:rs) 
                    | r <= p    = k (x:new') (prev' `revApp` xs) rs
                    | otherwise = shift new' (x:prev') xs rs
                revApp xs ys = foldl (flip (:)) ys xs

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

Дав вам несколько примеров, о которых вы просили, я скажу, что лучший способ начать оценивать Haskell - это прочитать документ, который дал мне идеи, которые мне нужны для написания DVD-пакета: Почему вопросы функционального программирования Джона Хьюза. Бумага на самом деле предшествует Haskell, но она блестяще объясняет некоторые идеи, которые делают людей такими, как Haskell.

Ответ 12

Я считаю, что для определенных задач я невероятно продуктивен с Haskell.

Причина кроется в кратковременном синтаксисе и простоте тестирования.

Вот что такое синтаксис объявления функции:

foo a = a + 5

Это самый простой способ определить функцию.

Если я напишу обратный

inverseFoo a = a - 5

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

prop_IsInverse:: Double → Bool
prop_IsInverse a = a == (inverseFoo $foo a)

И вызов из командной строки

jonny @ubuntu: runhaskell quickCheck + имена fooFileName.hs

Будет проверено, что все свойства в моем файле хранятся, случайным образом тестируя входные данные в сотни раз.

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

Ответ 13

Для меня привлекательность Haskell - это обещание гарантированной корректности компилятора. Даже если это для чистых частей кода.

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

Ответ 14

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

Ответ 15

он не имеет конструкций цикла. не многие языки имеют эту черту.

Ответ 16

Я согласен с тем, что функциональное программирование сводит ваш мозг к программированию под другим углом. Я использовал его только как любителя, но я думаю, что это коренным образом изменило подход к проблеме. Я не думаю, что был бы почти таким же эффективным с LINQ, не будучи подверженным Haskell (и используя генераторы и списки в Python).

Ответ 17

Чтобы отразить противоположный взгляд: Стив Йегге пишет, что Языки Hindely-Milner не обладают гибкостью, необходимой для написания хороших систем:

H-M очень красиво, полностью бесполезный формальный математический смысл. Это обрабатывает несколько вычислительных конструкций очень хорошо; соответствие шаблону отправка найдена в Haskell, SML и OCaml особенно удобен. Неудивительно, что он обрабатывает некоторые другие общие и весьма желательные конструкции неудобно в лучшем случае, но они объясняют эти сценарии прочь, сказав, что вы ошибаетесь, вы на самом деле не хочу их. Вы знаете, такие вещи, как, значения переменных.

Haskell заслуживает изучения, но он имеет свои недостатки.