【问题标题】:Custom Functor instance: Expected kind ‘* -> *’, but ‘AST’ has kind ‘*’自定义 Functor 实例:预期种类“* -> *”,但“AST”有种类“*”
【发布时间】:2017-07-21 10:52:03
【问题描述】:

我有这个相当简单的 ADT:

data AST = Node String [AST]
     | Leaf String
     | Empty
    deriving (Show)

还有这个 Functor 实例:

instance Functor AST where
    fmap f (Node s l) = Node (f s) (fmap f l)
    fmap f (Leaf s)   = Leaf (f s)
    fmap f Empty      = Empty

但是当我尝试编译它时,我得到了这个我完全不明白的错误:

Expected kind ‘* -> *’, but ‘AST’ has kind ‘*’
   • In the first argument of ‘Functor’, namely ‘AST’
     In the instance declaration for ‘Functor AST’

有人知道为什么会这样吗?我在 Internet 上找不到解决方案。

【问题讨论】:

  • 作为健全性检查,这个实例fmap 真的有什么作用吗? Functor 实例包含可以“映射”的东西,但在您的 AST 中没有这样的可映射数据。
  • 有分类意识的人应该注意,这里的 ASTfmap 定义构成了一个完全合理的分类函子,即使它们没有构成有效的 Haskell Functor

标签: haskell functor abstract-data-type


【解决方案1】:

仿函数作用于类型构造函数:如果你给它一个AST,它期望看到一个:

data AST a = ...
--       ^ type parameter

我们也可以在Functor 类的定义中看到这一点:

class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b

请注意,类头部的f 具有“kind* -> *,这意味着它充当某种函数,采用另一种类型(第一个 *)和产生一个类型(第二个*)。如您所见,fmap 将采用a -> b 类型的函数(我们无法控制b 是什么)。在您对fmap 的定义中,我们只能提供String -> String 函数。

现在将AST 设为函子没有多大意义,因为它不是函子。

但是,您可以轻松地将您的AST概括为:

data AST a = Node a [AST a]
     | Leaf a
     | Empty
    deriving (Show)

如果您使用该类型,AST String 等同于您对 AST 的旧定义。

列表[](也是Functor)也是如此。列表的定义是:

data [] a = [] | a : [a]

我们将Functor 定义为:

instance Functor [] where
    fmap _ [] = []
    fmap f (x:xs) = (f x) : (fmap f xs)

请注意,我们确实不是声明Functor [a],而是Functor []

【讨论】:

  • 次要观点:我想知道“高阶类型”是否是正确的术语。我想我以前从未遇到过这种情况,我会将它与(*->*)->* 之类的东西联系起来,类似于高阶函数。 be 的*->* 类型是参数类型、参数化类型或(在正确的上下文中)类型族、类型函数。
  • 谢谢,我不知道 Functor 必须是多态的。对于我的代码,我只对字符串版本感兴趣,所以我把它省略了
【解决方案2】:

函子必须是多态的,即data AST a = ...。在这种情况下,这就是“善良”的含义。它希望AST 不是一个类型,而是一个类型函数,接受一个类型并返回一个类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 2012-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多