Наскільки важливі передові концепції Haskell, такі як Monads та Applicative Functors для більшості рутинних завдань програмування?


16

Я прочитав книгу «Хаузл-хаскелл Learn You» до того моменту, коли вони представляють Monads і такі речі, як Just a. Вони настільки контрунтні для мене, що я просто відчуваю, що відмовляюся намагатися цього навчитися.

Але дуже хочеться спробувати.

Мені цікаво, чи можу я хоча б на деякий час уникнути вдосконалених концепцій і просто почати використовувати інші частини мови, щоб виконувати багато практичних завдань з більш основними частинами Haskell, тобто функціями, стандартним IO, обробкою файлів, взаємодію баз даних тощо.

Це розумний підхід до того, як навчитися користуватися Haskell?


2
Я не знаю, як далеко ви дістанетесь. Без монад ви не можете зробити нічого корисного в Haskell, оскільки робити все корисне в програмуванні (як, наприклад, написання серйозної програми, яку хтось насправді хотів би використовувати) вимагає вводу-виводу та стану, обом цим керувати можна лише в Haskell через використання монад.
Мейсон Уілер

1
@dan - я нарешті зрозумів монадів Хаскелла, прочитавши два документи - "Ти міг би винайти монади" та "Побороти незручний загін". Я не можу сказати, чи кращі вони за "Learn You a Haskell", якого я не читав, але вони працювали на мене. Щоб використовувати позначення do з монадою IO, ви можете дуже легко зрозуміти, що таке монади - ви зробите деякі речі, які змусять експертів або сміятися, або плакати, але поверхово, принаймні, це не дуже відрізняється від використання умовна імперативна мова. Але це варто отримати деякі більш глибоке розуміння.
Steve314

2
@dan, мені знадобилося майже десятиліття, щоб набриднути OOP і почати цінувати / цікавитись функціональними мовами. Я б все-таки навчився F # і Clojure, перш ніж спробувати Haskell серйозно спробувати. Хоча особисті виклики приємні, можна щось сказати про ваше відчуття кишки та шлях найменшого опору. Багато залежить від того, хто ти є. Якщо ви сильно матеріальний тип, ви, мабуть, хотіли б Haskell / Schem з самого початку. Якщо ви більше "хлопець", це теж добре. Не підходьте до Хаскелла зі ставленням "зробіть чи помріть". Продовжуй вивчати інші речі, а коли нудно повернешся.
робота

Дякую за пораду @Job. Я трохи спробував Clojure, але синтаксис Lisp справді не працює для мене як для читання, так і для введення тексту. Я вважаю синтаксис Хаскелла більш природним. Я із задоволенням тим часом використовую Ruby для проектів, але сподіваюся почати робити якісь практичні проекти в Haskell.
дан

@dan, Смішно, я вважаю синтаксис Clojure досить дружелюбним, але це, мабуть, тому, що я раніше піддавався схемі. Можливо, після F # я звикну до того, як Haskell робить справи.
робота

Відповіді:


16

Давайте спочатку розмежимо вивчення абстрактних понять та вивчення конкретних їх прикладів .

Ви не збираєтеся дуже далеко ігнорувати всі конкретні приклади з тієї простої причини, що вони абсолютно всюдисущі. Насправді абстракції існують значною мірою, тому що вони уніфікують те, що ви робили б у будь-якому випадку з конкретними прикладами.

З іншого боку, самі абстракції, безумовно, корисні , але вони не відразу необхідні. Можна досить далеко ігнорувати абстракції і просто використовувати різні типи безпосередньо. Ви хочете зрозуміти їх з часом, але ви завжди зможете повернутися до цього пізніше. Насправді я майже можу гарантувати, що якщо ви це зробите, коли ви повернетесь до нього, ви ляпнете по лобі і задумаєтесь, чому ви витратили весь цей час, роблячи речі важким шляхом, а не використовуючи зручні інструменти загального призначення.

Візьмемо Maybe aяк приклад. Це лише тип даних:

data Maybe a = Just a | Nothing

Це все, крім самодокументування; це необов'язкове значення. Або у вас є "просто" щось типу a, або у вас немає нічого. Скажімо, у вас є якась функція пошуку, яка повертається, Maybe Stringщоб відображати пошук Stringзначення, яке може бути відсутнім. Таким чином, ви узгоджуєте відповідність за значенням, щоб побачити, що це:

case lookupFunc key of
    Just val -> ...
    Nothing  -> ...

Це все!

Дійсно, нічого іншого вам не потрібно. Ні Functors, ні Monad, ні щось інше. Вони виражають загальні способи використання Maybe aзначень ... але вони просто ідіоми, "дизайнерські зразки", як би ви цього не хотіли назвати.

Єдине місце, якому ви справді не можете цього уникнути повністю, є IO, але все одно це загадкова чорна скринька, тому не варто намагатися зрозуміти, що це означає як Monadщось таке.

Насправді, ось чіт-лист для всього, про що ви справді потрібно знати IO:

  • Якщо щось має тип IO a, це означає, що це процедура , яка щось робить і випльовує aзначення.

  • Коли у вас є код коду, який використовує doнотації, напишіть щось подібне:

    do -- ...
       inp <- getLine
       -- etc...
    

    ... означає виконати процедуру праворуч від <-і призначити результат імені зліва.

  • Якщо у вас є щось подібне:

    do -- ...
       let x = [foo, bar]
       -- etc...
    

    ... це означає призначити значення простого виразу (а не процедури) праворуч від =імені зліва.

  • Якщо ви помістите щось там, не призначаючи значення, наприклад:

    do putStrLn "blah blah, fishcakes"
    

    ... це означає виконати процедуру і проігнорувати все, що вона повертає. Деякі процедури мають тип IO ()- ()тип - це такий собі заповнювач, який нічого не говорить, так що просто означає, що процедура щось робить і не повертає значення. Начебто voidфункція в інших мовах.

  • Виконання однієї і тієї ж процедури більше одного разу може дати різні результати; ось така ідея. Ось чому немає ніякого способу "вилучити" значення IOзі значення, тому що щось у IOзначенні не є значенням, це процедура отримання значення.

  • Останній рядок у doблоці повинен бути простою процедурою без призначення, де повернене значення цієї процедури стає зворотним значенням для всього блоку. Якщо ви хочете, щоб повернене значення використовувало якесь вже присвоєне значення, returnфункція приймає звичайне значення і надає вам процедуру неоперації, яка повертає це значення.

  • Крім цього, в цьому немає нічого особливого IO; ці процедури насправді самі по собі є простими значеннями, і ви можете передавати їх і комбінувати по-різному. Тільки коли вони виконуються в doблоці, який називається десь main, вони роблять все, що завгодно.

Отже, у чомусь подібному настільки нудною, стереотипною прикладною програмою:

hello = do putStrLn "What's your name?"
           name <- getLine
           let msg = "Hi, " ++ name ++ "!"
           putStrLn msg
           return name

... ви можете прочитати його так само, як і імперативна програма. Ми визначаємо процедуру з назвою hello. Після його виконання спочатку виконується процедура надрукування повідомлення із запитом вашого імені; далі він виконує процедуру, яка зчитує рядок введення, і присвоює результат name; тоді він присвоює вираз імені msg; потім він друкує повідомлення; тоді він повертає ім'я користувача як результат усього блоку. Оскільки nameце a String, це означає, що helloце процедура, яка повертає a String, тому вона має тип IO String. А тепер ви можете виконати цю процедуру в іншому місці, як і вона getLine.

Пффф, монади. Кому їх потрібно?


2
@dan: Вас дуже вітають! Якщо у вас є якісь конкретні питання по дорозі, не соромтесь задати питання про переповнення стека. Тег [haskell] там, як правило, досить активний, і якщо ви пояснюєте, звідки ви приїжджаєте від людей, то це дуже добре допомагає вам зрозуміти, а не просто підказувати критичні відповіді.
CA McCann

9

Наскільки важливі передові концепції Haskell, такі як Monads та Applicative Functors для більшості рутинних завдань програмування?

Вони є важливими. Без них все занадто просто використовувати Haskell як просто іншу імперативну мову, і хоча Саймон Пейтон Джонс колись називав Haskell "найкращою імперативною програмою мови", ви все одно не вистачаєте з найкращої частини.

Монади, перетворення Монади, моноїди, функтори, додаткові функціонери, контраваріантні функціонери, стрілки, категорії тощо - це схеми дизайну. Після того, як ви помістите конкретну річ, яку ви робите в одну з них, ви зможете скористатися величезним набором функцій, написаних абстрактно. Ці класи не просто там, щоб вас бентежити, вони там, щоб полегшити вам життя та зробити вас більш продуктивними. На мій погляд, вони є найбільшою причиною стиснення хорошого коду Хаскелла.


1
+1: Дякуємо за те, що наголосили, що "Монади, Монада перетворює, моноїди, функціонери, додаткові функціонери, протирічні функціонери, стрілки, категорії тощо - це моделі дизайну". !
Джорджіо

7

Це вкрай необхідно, якщо ви просто хочете щось запустити? Ні.

Чи потрібно, якщо ви хочете написати прекрасні ідіоматичні програми Haskell? Абсолютно.

При програмуванні в Haskell це допомагає пам’ятати про дві речі:

  1. Система типу існує, щоб допомогти вам. Якщо ви складаєте свою програму з високополіморфного коду типу важкого класу, дуже часто ви можете потрапити в чудову ситуацію, коли тип потрібної функції приведе вас до правильної реалізації (одного єдиного!) .

  2. Різні доступні бібліотеки були розроблені за принципом №1.

Отже, коли пишу програму в Haskell, я починаю з написання типів. Потім я починаю заново і записую ще кілька загальних типів (і якщо вони можуть бути екземплярами існуючих класів, тим краще). Потім я записую деякі функції, які отримують мені значення тих типів, які я хочу. (Тоді я граю з ними в ghciі, можливо, пишу кілька тестів для швидкої перевірки.) І, нарешті, я склею все це разом main.

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

Удачі!


1
Дякую! Я коливався між Клуджуре і Хаскеллом, але тепер я схиляюся до Хаскелла через те, наскільки такі корисні вам люди.
дан

1
Громада Haskell здається дуже приємною та корисною. І, здається, звідки походить багато цікавих ідей
Захарій К
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.