Как вы используете Control.Applicative для написания чистых Haskell? - PullRequest
60 голосов
/ 20 января 2010

В недавнем ответе на вопрос стиля я написал

main = untilM (isCorrect 42) (read `liftM` getLine)

и

isCorrect num guess =
  case compare num guess of
    EQ -> putStrLn "You Win!" >> return True
    ...

Мартейн услужливо предложенные альтернативы:

main = untilM (isCorrect 42) (read <$> getLine)

EQ -> True <$ putStrLn "You Win!"

Какие общие шаблоны в коде на Haskell можно сделать более понятными, используя абстракции из Control.Applicative ? Какие полезные практические правила следует иметь в виду для эффективного использования Control.Applicative?

Ответы [ 3 ]

48 голосов
/ 28 июля 2012

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

Если вы используете do -обозначение и ваши сгенерированные значения [1] не используются в выражениях, которые вы упорядочиваете [2], то этот код может преобразоваться в стиль Applicative. Аналогично, если вы используете одно или несколько сгенерированных значений в выражении, которое упорядочено, то вы должны использовать Monad, а Applicative недостаточно для достижения того же кода.

Например, давайте посмотрим на следующий код:

do a <- e1
   b <- e2
   c <- e3
   return (f a b c)

Мы видим, что ни в одном из выражений справа от <- не появляется ни одно из сгенерированных значений (a, b, c). Поэтому мы можем преобразовать его в использование кода Applicative. Вот одно из возможных преобразований:

f <$> e1 <*> e2 <*> e3

и еще:

liftA3 f e1 e2 e3

С другой стороны, возьмите этот фрагмент кода, например:

do a <- e1
   b <- e2 a
   c <- e3
   return (f b c)

Этот код не может использовать Applicative [3], потому что сгенерированное значение a будет использовано позже в выражении в понимании. Для получения результата нужно использовать Monad - попытаться включить его в Applicative, чтобы понять, почему.

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

[1] Те, которые появляются слева от <-.

[2] Выражения, которые появляются справа от <-.

[3] Строго говоря, его части могли бы, с учетом e2 a.

44 голосов
/ 21 января 2010

В основном, монады также являются аппликативными функторами [1]. Поэтому, когда вы обнаружите, что используете liftM, liftM2 и т. Д., Вы можете объединить вычисления вместе, используя <*>. В некотором смысле вы можете думать о аппликативных функторах как об аналогах функций. Чистую функцию f можно поднять, выполнив f <$> x <*> y <*> z.

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

import Control.Applicative

ifte condition trueClause falseClause = do
  c <- condition
  if c then trueClause else falseClause

x = ifte (return True) (putStrLn "True") (putStrLn "False")

ifte' condition trueClause falseClause = 
  if condition then trueClause else falseClause

y = ifte' <$> (pure True) <*> (putStrLn "True") <*> (putStrLn "False")

x только выводит True, тогда как y выводит True и False последовательно.

[1] Typeclassopedia . Настоятельно рекомендуется.

[2] http://www.soi.city.ac.uk/~ross/papers/Applicative.html. Несмотря на то, что это академическая статья, за ней не трудно следить.

[3] http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors. Объясняет сделку очень хорошо.

[4] http://book.realworldhaskell.org/read/using-parsec.html#id652399. Показывает, как монадическая библиотека Parsec может также использоваться аппликативно.

10 голосов
/ 23 января 2010
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...