Haskell类型推理(ReaderT和元组)



探索此材料:茶叶上的镜头 我遇到了一个有趣的(一开始很简单(点:

ex3 :: (a, b) -> (b, a)
ex3 = do
a <- fst
b <- snd
return (b, a)

一切都很好,但是这个函数使用什么类型的monad(因为我们里面有一个do块(。经过几次尝试,我得出了这个结论:

ex2 :: ReaderT (a, b) ((,) b) a
ex2 = ReaderT $ do
a <- fst
b <- snd 
return (b, a)
ex3 :: (a, b) -> (b, a)
ex3 = runReaderT ex2

因此,我们有使用内部monad((,(b(的ReaderT。有趣的是 - 我对此不够满意,并决定不使用 do 符号重写 ex2。这就是我得到的:

ex2 :: Monoid b => ReaderT (a, b) ((,) b) a
ex2 = ReaderT $ 
pair -> return (fst pair) >>= 
a -> return (snd pair) >>= 
b -> (b, a) 

甚至:

ex2 :: Monoid b => ReaderT (a, b) ((,) b) a
ex2 = ReaderT $ 
pair -> (mempty, fst pair) >>= 
a -> (mempty, snd pair) >>= 
b -> (b, a) 

两种变体都需要 b 具有 Monoid 类型限制。 问题是:我可以只使用 (>>=( 而不使用 Monoid 限制来编写这个函数吗 - 就像我们对 do 符号变体所做的那样?显然,无论有没有do符号,我们都做同样的事情。也许是偶数的区别,我们必须在第二步的每一步构造单子和口渴的功能,这要求我们声明"b"应该是一个幺半群 - 一些幺半群。在第一种情况下,我们只是从某个monad中提取我们的值 - 而不是构建它们。谁能解释一下我的想法是否正确?

谢谢!!

您还没有将其从do表示法完全脱糖到(>>=)调用。直接翻译如下所示:

ex2 :: ReaderT (a, b) ((,) b) a
ex2 = ReaderT $
fst >>= (a ->         -- a <- fst
snd >>= (b ->         -- b <- snd
return (b, a)))        -- return (b, a)

此外,您实际上并没有使用(,) b的单元性,即使它适合此处的插槽,用于ReaderT的"内一元"。

ex3 :: (a, b) -> (b, a)

表示,前缀表示法

ex3 :: (->) (a, b) (b, a)
-----------m
------t

因此,monad 是m = (->) (a, b)它是 Reader monad(直到同构(,一对作为其隐式参数/只读状态。

你不需要幺半群。普通的读者单子就足够了。如果要使用ReaderT,请使用单位 monad 作为内部 monad。

ex2 :: Monoid b => ReaderT (a, b) Identity (b, a)
ex2 = ReaderT $ 
pair -> Identity (fst pair) >>= 
a -> Identity (snd pair) >>= 
b -> Identity (b, a) 

当然,上面的代码可以简化。

总结一下:

  1. monad 的类型是 (->( r - 只是函数或简单的阅读器;
  2. 如何在不做的情况下脱糖初始功能:

    ex3' :: (a, b) -> (b, a)
    ex3' = fst >>=
    a -> snd >>=
    b -> return (b, a)
    

最新更新