haskell 为什么这个免费的单子例子失败了?

whlutmcx  于 2023-10-19  发布在  其他
关注(0)|答案(2)|浏览(118)

我试图理解Free monads在Haskell中是如何工作的,为此我一直在尝试做一个例子。我的代码基于Philip JF的答案here。下面是一个例子:

data Free f a = Pure a | Roll (f (Free f a))
--it needs to be a functor
instance Functor f => Functor (Free f) where
  fmap f (Pure a) = Pure (f a)
  fmap f (Roll x) = Roll (fmap (fmap f) x)

--this is the same thing as (++) basically
concatFree :: Functor f => Free f (Free f a) -> Free f a
concatFree (Pure x) = x
concatFree (Roll y) = Roll (fmap concatFree y)

data F a = One a | Two a a | Two' a a | Three Int a a a
  deriving Show

-- Lift F into the Free monad
type FreeF a = Free F a

tree :: FreeF String
tree = Roll (One (Pure "A"))

example1 :: F (FreeF String)
example1 = One (Roll (One (Pure "A")))

上面的代码是有效的。然后我想做的是提出一个FreeF (f (FreeF a)),以便将concatFree函数应用于它。这就是我遇到麻烦的地方。

result :: FreeF (FreeF String)
result = Roll (One (Roll (One (Pure "A"))))

上面的代码不起作用。在我看来,这种结构是正确的。我错过了什么?
更准确地说,当我尝试用ghci运行这个函数时,我得到了错误:

FreeMonads3.hs:25:10: error:
    • Couldn't match type ‘[Char]’ with ‘Free F String’
      Expected type: FreeF (FreeF String)
        Actual type: Free F [Char]
    • In the expression: Roll (One (Roll (One (Pure "A"))))
      In an equation for ‘result’:
          result = Roll (One (Roll (One (Pure "A"))))
   |
25 | result = Roll (One (Roll (One (Pure "A"))))
gfttwv5a

gfttwv5a1#

在你的表情中:

result :: FreeF (FreeF String)
result = Roll (One (Roll (One (Pure "A"))))

RollOne构造函数的所有示例都在第一级FreeF类型内部操作。
正如注解中提到的,需要第二个Pure运算符才能进入FreeF (FreeF String)类型。就像这样:

result :: FreeF (FreeF String)
result = Roll (One ( Pure (Roll (One (Pure "A")))))

附注1

通过利用Haskell提供的.函数组合操作符和$低优先级函数调用操作符,可以避免上面的重括号嵌套。
就像这样:

result1 :: FreeF (FreeF String)
result1 = Roll $ One $ Pure $ Roll $ One $ Pure "A"

或者像这样

result2 :: FreeF (FreeF String)
result2 = Roll . One . Pure . Roll . One . Pure  $  "A"

请注意,上面的Roll构造函数的两个示例具有不同的类型。对于OnePure构造函数也是如此。例如,最左边的Pure构造函数的类型是:
FreeF String -> FreeF (FreeF String)

要有一个合适的自由monad,你需要确保你的F类型构造函数有一个Functor示例。确保这一点的最便宜的方法是:

{-#  LANGUAGE  DeriveFunctor  #-}

data F a = One a | Two a a | Two' a a | Three Int a a a
  deriving (Show, Functor)

但是,作为初学者,手动编写fmap的相关代码作为练习可能是值得的。

bkhjykvo

bkhjykvo2#

您定义的result类型实际上是:

result :: FreeF String
result = Roll (One (Roll (One (Pure "A"))))

这是因为Roll的类型(专用于FreeF)是F (FreeF a) -> FreeF a
要获得所需的类型,请使用Pure而不是RollPure的类型为a -> FreeF a

result :: FreeF (FreeF String)
result = Pure (One (Roll (One (Pure "A"))))

相关问题