Haskell类型推断(ReaderT和元组)

时间:2018-08-22 16:34:09

标签: haskell monads

探索这种材料:Lens over tea我遇到了一个有趣的(起初很简单)的观点:

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

一切都很好,但是此函数使用哪种类型的monad(因为我们内部有一个do-block)。经过几次尝试,我得出了以下结论:

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-notation重写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-notation变体一样?显然,无论有无注释,我们都可以这样做。也许甚至是不同的是,我们必须在第二和第三功能的每一步都构造单子,这要求我们声明“ b”应该是一个单半体-一些单半体。在第一种情况下,我们只是从某个单子中提取我们的值,而不是构建它们。谁能解释我在朝着正确的方向思考吗?

谢谢!

3 个答案:

答案 0 :(得分:2)

您尚未完全将其从do表示法转换为(>>=)调用。直接翻译如下:

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

此外,即使它适合(,) b的“内部monad”插槽,您实际上并没有使用ReaderT的monadness。

答案 1 :(得分:1)

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

是指前缀符号

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

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

您不需要Monoid。普通读者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) 

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

答案 2 :(得分:0)

所以总结一下:

  1. monad的类型是(->)r-只是函数或简单的阅读器;
  2. 如何不执行初始功能就对糖进行去糖处理:

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