Примеры использования для экземпляров functor/applative/monad для функций
Haskell имеет Functor
, Applicative
и Monad
экземпляры, определенные для функций (в частности, частично применяемого типа (->) a
) в стандартной библиотеке, построенных вокруг композиции функций.
Понимание этих случаев - это отличное упражнение с умственным уклоном, но мой вопрос здесь о практическом использовании этих экземпляров. Я был бы рад услышать о реалистичных сценариях, где люди использовали их для практического кода.
Ответы
Ответ 1
Общий шаблон, который включает в себя Functor и аппликативные экземпляры функций, например, (+) <$> (*2) <*> (subtract 1)
. Это особенно полезно, когда вам нужно подавать серию функций с одним значением. В этом случае приведенное выше эквивалентно \x -> (x * 2) + (x - 1)
. Хотя это очень близко к LiftA2
, вы можете продлить этот шаблон на неопределенный срок. Если у вас есть функция f, чтобы взять 5 параметров, таких как a -> a -> a -> a -> a -> b
, вы можете сделать, например, f <$> (+2) <*> (*2) <*> (+1) <*> (subtract 3) <*> (/2)
и передать его одним значением. Как и в следующем случае;
Prelude> (,,,,) <$> (+2) <*> (*2) <*> (+1) <*> (subtract 3) <*> (/2) $ 10
(12.0,20.0,11.0,7.0,5.0)
Изменить: Кредит для повторного комментария @Will Ness для комментария моей по другой теме, здесь идет красивое использование аппликативных функций;
Prelude> let isAscending = and . (zipWith (<=) <*> drop 1)
Prelude> isAscending [1,2,3,4]
True
Prelude> isAscending [1,2,5,4]
False
Ответ 2
Иногда вы хотите рассматривать функции формы a -> m b
(где m
является Applicative
) как Applicative
. Это часто случается при написании валидаторов или парсеров.
Один из способов сделать это - использовать Data.Functor.Compose
, который контактирует с экземплярами Applicative
(->) a
и m
, чтобы предоставить экземпляр Applicative
для композиции:
import Control.Applicative
import Data.Functor.Compose
type Star m a b = Compose ((->) a) m b
readPrompt :: Star IO String Int
readPrompt = Compose $ \prompt -> do
putStrLn $ prompt ++ ":"
readLn
main :: IO ()
main = do
r <- getCompose (liftA2 (,) readPrompt readPrompt) "write number"
print r
Существуют и другие способы, например создание собственного типа new, или использование готового newtypes из базы или других библиотек.