Вы не упомянули об этом в вопросе, но я думаю, вы специально думали об использовании пары для определения Reader
, потому что также имеет смысл думать об этом как о способе предоставления фиксированной среды. Допустим, у нас есть более ранний результат в монаде Reader
:
return 2 :: Reader Integer Integer
Мы можем использовать этот результат для дальнейших вычислений с фиксированной средой (и методы Monad
гарантируют, что он останется фиксированным на протяжении всей цепочки (>>=)
):
GHCi> runReader (return 2 >>= \x -> Reader (\r -> x + r)) 3
5
(Если вы подставите определения return
, (>>=)
и runReader
в приведенное выше выражение и упростите его, вы точно увидите, как оно сводится к 2 + 3
.)
Теперь давайте последуем вашему предложению и определим:
newtype Env r a = Env { runEnv :: (r, a) }
Если у нас есть среда типа r
и предыдущий результат типа a
, мы можем сделать из них Env r a
...
Env (3, 2) :: Env Integer Integer
... и мы также можем получить новый результат:
GHCi> (\(r, x) -> x + r) . runEnv $ Env (3, 2)
5
Тогда возникает вопрос, можем ли мы зафиксировать этот шаблон через интерфейс Monad
. Ответ - нет. Хотя существует экземпляр Monad
для пар, он делает совсем другое:
newtype Writer r a = Writer { Writer :: (r, a) }
instance Monoid r => Monad (Writer r) where
return x = (mempty, x)
m >>= f = Writer
. (\(r, x) -> (\(s, y) -> (mappend r s, y)) $ f x)
$ runWriter m
Ограничение Monoid
необходимо для того, чтобы мы могли использовать mempty
(что решает проблему, которую вы заметили, связанную с созданием r_unknown
из ниоткуда) и mappend
(что позволяет комбинировать первые элементы пары таким образом, t нарушают законы монад). Однако этот экземпляр Monad
делает что-то совершенно отличное от того, что делает Reader
. Первый элемент пары не является фиксированным (он может быть изменен, поскольку мы mappend
присваиваем ему другие сгенерированные значения), и мы не используем его для вычисления второго элемента пары (в приведенном выше определении y
не используется). не зависят ни от r
, ни от s
). Writer
— регистратор; значения r
здесь выводятся, а не вводятся.
Однако есть один способ, в котором ваша интуиция оправдывается: мы не можем создать монаду, подобную читателю, используя пару, но мы можем создать монаду, подобную читателю, co. Проще говоря, Comonad
вот что получится, если перевернуть интерфейс Monad
вверх дном:
-- This is slightly different than what you'll find in Control.Comonad,
-- but it boils down to the same thing.
class Comonad w where
extract :: w a -> a -- compare with return
(=>>) :: w a -> (w a -> b) -> w b -- compare with (>>=)
Мы можем дать Env
, от которого мы отказались, экземпляр Comonad
:
newtype Env r a = Env { runEnv :: (r, a) }
instance Comonad (Env r) where
extract (Env (_, x)) = x
w@(Env (r, _)) =>> f = Env (r, f w)
Это позволяет нам написать пример 2 + 3
с самого начала в терминах (=>>)
:
GHCi> runEnv $ Env (3, 2) =>> ((\(r, x) -> x + r) . runEnv)
(3,5)
Один из способов понять, почему это работает, — заметить, что функция a -> Reader r b
(т. е. то, что вы даете (>>=)
из Reader
) — это, по сути, то же самое, что и функция Env r a -> b
(то есть то, что вы даете (=>>)
из Env
):
a -> Reader r b
a -> (r -> b) -- Unwrap the Reader result
r -> (a -> b) -- Flip the function
(r, a) -> b -- Uncurry the function
Env r a -> b -- Wrap the argument pair
В качестве еще одного доказательства этого, вот функция, которая превращает одно в другое:
GHCi> :t \f -> \w -> (\(r, x) -> runReader (f x) r) $ runEnv w
\f -> \w -> (\(r, x) -> runReader (f x) r) $ runEnv w
:: (t -> Reader r a) -> Env r t -> a
GHCi> -- Or, equivalently:
GHCi> :t \f -> uncurry (flip (runReader . f)) . runEnv
\f -> uncurry (flip (runReader . f)) . runEnv
:: (a -> Reader r c) -> Env r a -> c
Чтобы подвести итог, вот немного более длинный пример с версиями Reader
и Env
рядом:
GHCi> :{
GHCi| flip runReader 3 $
GHCi| return 2 >>= \x ->
GHCi| Reader (\r -> x ^ r) >>= \y ->
GHCi| Reader (\r -> y - r)
GHCi| :}
5
GHCi> :{
GHCi| extract $
GHCi| Env (3, 2) =>> (\w ->
GHCi| (\(r, x) -> x ^ r) $ runEnv w) =>> (\z ->
GHCi| (\(r, x) -> x - r) $ runEnv z)
GHCi| :}
5
person
duplode
schedule
18.02.2017
Reader
. - person duplode   schedule 18.02.2017