【问题标题】:Making continuation monad wrapper instance of Monad class制作 Monad 类的延续 monad 包装器实例
【发布时间】:2015-03-14 13:28:38
【问题描述】:

我有 Foo 类型,它是 Cont a a 的简单包装。我想让Foo 键入Monad 类的实例。我试试这个:

import Control.Monad.Cont

newtype Foo a = Foo {unFoo :: Cont a a}

instance Monad Foo where
    return = Foo . return
    Foo inner >>= func = Foo (inner >>= newFunc)
        where newFunc x = (unFoo $ func x)

但是我收到了这个错误:

Couldn't match type `a' with `b'
  `a' is a rigid type variable bound by
      the type signature for >>= :: Foo a -> (a -> Foo b) -> Foo b
      at Classes.hs:7:5
  `b' is a rigid type variable bound by
      the type signature for >>= :: Foo a -> (a -> Foo b) -> Foo b
      at Classes.hs:7:5
Expected type: ContT b Data.Functor.Identity.Identity a
  Actual type: Cont a a
In the first argument of `(>>=)', namely `inner'
In the first argument of `Foo', namely `(inner >>= newFunc)'
In the expression: Foo (inner >>= newFunc)

如何正确为Foo添加Monad实例?

【问题讨论】:

    标签: class haskell instance monads continuations


    【解决方案1】:

    您不能将Foo 变成Monad

    首先,让我们指出Foo a(a -> a) -> a 的一种精心编写的方式。

    runFoo :: Foo a -> ((a -> a) -> a)
    runFoo = runCont . unFoo
    
    foo :: ((a -> a) -> a) -> Foo a
    foo = Foo . cont
    

    只有一种方法可以定义(>>=) :: Foo a -> (a -> Foo b) -> Foo b。我们需要一个a 来传递给箭头a -> Foo b。我们唯一拥有as 的是Foo a,它相当于(a -> a) -> a。如果我们可以提供a -> a 类型的函数,它将给我们一个a,它只有一个id。所以我们获得a的唯一选择是通过id

    instance Monad Foo where
        return = Foo . return
        ma >>= f = f (runFoo ma id)
    

    这将违反Monad 法律之一,m >>= return ≡ m。我们将写一个反例,它位于Foo

    counterExample :: Foo Int
    counterExample = foo (\f -> if f 0 == 1 then 7 else 13)
    

    当传递标识函数 id 时,反例结果为 13,但传递后继函数 (+1) 时只有 7

    print $ runFoo counterExample id
    print $ runFoo counterExample (+1)
    
    13
    7
    

    由于Monad 法律,counterExample' = counterExample >>= return 应该与counterExample 完全相同,但不可能。 >>= 已经将id 传递给函数,并且只有returned 结果13counterExample'counterExample 做的事情不同。

    let counterExample' = counterExample >>= return
    print $ runFoo counterExample' id
    print $ runFoo counterExample' (+1)
    
    13
    14
    

    由于>>= 只有一种可能的实现,而且它不正确,因此Foo 没有正确的Monad 实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-26
      • 2012-10-16
      • 1970-01-01
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 2018-03-15
      相关资源
      最近更新 更多