Подтвердить что ты не робот

Как использовать (->) экземпляры Monad и путаницу о (->)

В разных вопросах я нашел подсказки в комментариях относительно использования экземпляра (->) Monads, например. для реализации точечного стиля.

Что касается меня, это немного абстрактно. Хорошо, я видел экземпляры Arrow на (->), и мне кажется, что (->) может использоваться в нотации экземпляра, но не в объявлениях типа (это было бы только вещество для другого вопроса).

Есть ли примеры с использованием (->) в качестве экземпляра Monad? Или хорошая ссылка?

Извините, если этот вопрос, возможно, уже обсуждался здесь, но поиск "(->) Monad instance" дает вам много хитов, как вы можете себе представить... поскольку почти каждый вопрос о Haskell где-то включает (->) или "Monad".

4b9b3361

Ответ 1

Для заданного типа r функцию типа r -> a можно рассматривать как вычисление, предоставляющее a, используя среду, набранную r. Учитывая две функции r -> a и a -> (r -> b), легко представить, что их можно составить при заданной среде (опять же, типа r).

Но подождите! Именно о том, что такое монады!

Итак, мы можем создать экземпляр Monad для (->) r, который реализует f >>= g, передавая r как на f, так и на g. Это то, что делает экземпляр Monad для (->) r.

Чтобы на самом деле получить доступ к среде, вы можете использовать id :: r -> r, о которой вы теперь можете думать как о вычислении, выполняющемся в среде r и предоставляя r. Чтобы создать локальные подрежимы, вы можете использовать следующее:

inLocalEnvironment :: (r -> r) -> (r -> a) -> (r -> a)
inLocalEnvironment xform f = \env -> f (xform env)

Этот шаблон наличия среды, переданной вычислениям, которые могут затем запросить его и изменить локально, полезен не только для монады (->) r, поэтому он абстрагируется в класс MonadReader, используя гораздо более разумные имена чем я здесь использовал:

http://hackage.haskell.org/packages/archive/mtl/2.0.1.0/doc/html/Control-Monad-Reader-Class.html

В принципе, он имеет два экземпляра: (->) r, которые мы видели здесь, и ReaderT r m, который является всего лишь оберткой newtype вокруг r -> m a, поэтому это то же самое, что и (->) r monad я описанный здесь, за исключением того, что он выполняет вычисления в другой, преобразованной монаде.

Ответ 2

Чтобы определить монаду для (->) r, нам нужны две операции: return и (>>=), подчиненные трем законам:

instance Monad ((->) r) where

Если мы посмотрим на подпись возврата для (->) r

    return :: a -> r -> a

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

    return a r = a

Или поочередно,

    return = const

Чтобы построить (>>=), если мы специализируем его подпись типа с монадой (->) r,

    (>>=) :: (r -> a) -> (a -> r -> b) -> r -> b

действительно существует только одно возможное определение.

    (>>=) x y z = y (x z) z

Использование этой монады подобно передаче по дополнительному аргументу r каждой функции. Вы можете использовать это для конфигурации или передать параметры вниз в глубину своей программы.

Мы можем проверить, что это монада, проверив три закона монады:

1. return a >>= f = f a 

return a >>= f 
= (\b -> a) >>= f -- by definition of return
= (\x y z -> y (x z) z) (\b -> a) f -- by definition of (>>=)
= (\y z -> y ((\b -> a) z) z) f -- beta reduction
= (\z -> f ((\b -> a) z) z) -- beta reduction
= (\z -> f a z) -- beta reduction
= f a -- eta reduction

2. m >>= return = m

m >>= return
= (\x y z -> y (x z) z) m return -- definition of (>>=)
= (\y z -> y (m z) z) return -- beta reduction
= (\z -> return (m z) z) -- beta reduction
= (\z -> const (m z) z) -- definition of return
= (\z -> m z) -- definition of const
= m -- eta reduction

Последний закон монады:

3. (m >>= f) >>= g  ≡  m >>= (\x -> f x >>= g)

следует аналогичным, легким экваториальным рассуждениям.

Можно также определить ряд других классов для ((- > ) r), таких как Functor,

instance Functor ((->) r) where

и если мы посмотрим на подпись

   -- fmap :: (a -> b) -> (r -> a) -> r -> b

мы видим, что его справедливая композиция!

   fmap = (.)

Аналогично мы можем сделать экземпляр Applicative

instance Applicative ((->) r) where
   -- pure :: a -> r -> a
   pure = const

   -- (<*>) :: (r -> a -> b) -> (r -> a) -> r -> b
   (<*>) g f r = g r (f r)

Что приятно иметь эти экземпляры, они позволяют использовать все Monad и Applicative при манипулировании функциями.

Существует множество примеров классов, включающих (- > ), например, вы можете вручную записать экземпляр Monoid для (b → a), учитывая моноид на a как:

enter code here
instance Monoid a => Monoid (b -> a) where
    -- mempty :: Monoid a => b -> a
    mempty _ = mempty
    -- mappend :: Monoid a => (b -> a) -> (b -> a) -> b -> a
    mappend f g b = f b `mappend` g b

но с учетом экземпляра Monad/Applicative вы также можете определить этот экземпляр с помощью

instance Monoid a => Monoid (r -> a) where
    mempty = pure mempty
    mappend = liftA2 mappend

используя аппликативный экземпляр для (->) r или

instance Monoid a => Monoid (r -> a) where
    mempty = return mempty
    mappend = liftM2 mappend

используя экземпляр Monad для (->) r.

Здесь сбережения минимальны, но, например, инструмент @pl для создания точечного кода, который предоставляется lambdabot на IRC-канале #haskell, довольно редко злоупотребляет этими экземплярами.