首先,您收到的错误是 kind 错误。种类是类型的“类型”。正如类型对值进行分类一样,种类对类型进行分类。就像 Haskell 从值推断类型一样,它也从类型推断类型。我们使用相同的符号:: 来表示一个值具有某种类型(例如,1 :: Int)并表示一个类型具有某种类型(例如,Int :: *)。
错误信息中提到了两种:*是被值所占据的类型,例如Int和Bool,而* -> *是类型构造函数 例如Maybe、[] 和IO。您可以将类型构造函数视为类型级函数:Maybe 将类型(*)作为参数并返回一个类型(也是*)作为结果,例如Maybe Int :: *。种类的柯里化方式与函数相同;例如,Either 具有 * -> * -> * 类型,因为它需要两个 * 类型的参数来生成类型 * 的类型:
Either :: * -> * -> *
Either Int :: * -> *
Either Int Bool :: *
所以错误来自Show 实例的类型类约束:
instance (Show m, Show r) => Show (Thread m r) where
------
Show 是可以显示的* 类型的类。您可以通过在 GHCi 中输入:kind Show(或:k Show)来查看:
> :kind Show
Show :: * -> Constraint
所以Show 采用一种* 的类型并返回一个类型类约束。在不涉及约束的太多细节的情况下,这意味着Show m 意味着m :: *。但是,Thread 的定义在其Atomic 构造函数的定义中将参数传递给m,该构造函数具有m (Thread m r) 类型的字段。看看Thread的那种:
> :kind Thread
Thread :: (* -> *) -> * -> *
这意味着m :: * -> *,因此不匹配。
下一个错误是在你的Show实例的实现中,即:
show (Atomic m x) = "Atomic " ++ show m ++ " " ++ show x
- ----------------
在这里,您提供了一个匹配多个字段的模式,但 Atomic 只有一个字段。您应该将实现更改为以下内容:
show (Atomic m) = "Atomic " ++ show m
如果您删除 Show m 约束,您将看到更有用的错误消息:
Could not deduce (Show (m (Thread m r)))
arising from a use of ‘show’
from the context (Show r)
bound by the instance declaration at …
In the second argument of ‘(++)’, namely ‘show m’
In the expression: "Atomic " ++ show m
In an equation for ‘show’: show (Atomic m) = "Atomic " ++ show m
这表示您尝试在 m (Thread m r) 类型的值上调用 show,但您在上下文中没有该约束。所以你可以添加它:
instance (Show (m (Thread m r)), Show r) => Show (Thread m r) where
---------------------
这不是“标准”Haskell,因此 GHC 开始建议允许它的扩展:
Non type-variable argument in the constraint: Show (m a)
(Use FlexibleContexts to permit this)
In the context: (Show (m a), Show r)
While checking an instance declaration
In the instance declaration for ‘Show (Thread m r)’
让我们尝试添加-XFlexibleContexts(在命令行中使用ghci … -XFlexibleContexts,在会话中使用:set -XFlexibleContexts,或者在源文件中使用{-# LANGUAGE FlexibleContexts #-}),因为它实际上是一个非常良性的扩展。现在我们得到一个不同的错误:
Variable ‘a’ occurs more often than in the instance head
in the constraint: Show (m a)
(Use UndecidableInstances to permit this)
In the instance declaration for ‘Show (Thread m r)’
我们可以添加-XUndecidableInstances——这意味着您正在编写 GHC 无法证明会停止的类型级计算。有时这是不可取的,但在这种情况下很好,因为我们知道实例解析要么找到可接受的Show 实例,要么失败。现在编译器接受了它,我们可以尝试我们的Show 实例,比如m ~ [] 和r ~ Int 这样简单的例子:
> Atomic [Atomic [Return 1, Return 2]] :: Thread [] Int
Atomic [Atomic [Return 1,Return 2]]
但是,请注意,当您将 m 设置为没有任何 Show 实例的类型构造函数时,这将不起作用,例如 IO:
> Atomic (return (Atomic (return (Return 1) >> return (Return 2)))) :: Thread IO Int
No instance for (Show (IO (Thread IO Int)))
arising from a use of ‘print’
In a stmt of an interactive GHCi command: print it
此外,您可能还注意到您的 Show 实例的结果中缺少一些括号:
> Atomic (Right (Atomic (Left "asdf"))) :: Thread (Either String) Int
Atomic Right Atomic Left "asdf"
这是一个简单的解决方法,我将留给你。
这应该使您的实例能够使用文章中的Toy 等数据类型,通过Free:
> Atomic (Free (Output "foo" (Pure (Return "bar"))))
Atomic (Free (Output "foo" (Pure Return ("bar"))))