Haskell fmap 自定义数据类型

Haskell fmap over custom data type

在我的项目中,我有一些数据类型如下所示

data StructureA = StructureA [Int] Bool Int
data StructureB = StructureB [String] String

我的目标是将函数映射到此类数据类型实例中包含的数组

inst = StructureA [1,1,1] True 0
fmap (+1) inst -- [2,2,2]

我最初的解决方案是这样的

instance Functor StructureA where
    fmap (StructureA arr _ _) = fmap arr

但是,我收到类型不匹配错误。

问题是我如何声明一个可以多态应用于此类数据结构的函数?

您只能在 参数化类型 (准确地说是 * -> *)上声明一个 Functor 实例:一个仍然需要额外(和恰好一个)类型参数。

所以首先我们需要引入一个类型参数。即使你从不打算使用 Ints 之外的东西,我们也可以很容易地将它抽象出来:

data Structure <b>a</b> = Structure [<b>a</b>] Bool Int

例如,我们可以将 StructureA 声明为 类型同义词 :

type StructureA = Structure Int

现在我们可以使它成为一个 Functor 实例,写成:

instance Functor <b>Structure</b> where
    fmap f (Structure as b c) = ...

请注意,我们这里 不是 (Structure a),而是写 Structure,因为 - 正如我们已经说过的 - fmap 有自由更改集合工作的类型:f 函数可以有例如类型 Int -> CharStructure Int 转换为 Structure Char.

现在还需要实施fmapfmap 的类型为 fmap :: Functor f => (a -> b) -> f a -> f b,这意味着它接受一个函数,在本例中为 Structure a,并构造一个 Structure b。根据您的问题(以及我们做出的设计决策),我们唯一可以映射的部分是第一个参数,因此我们构造一个新的 Structure,其中第二个参数是 fmap f 的结果,然后在第二个参数上,所以:

instance Functor Structure where
    fmap f (Structure as b c) = <b>Structure (fmap f as) b c</b>

只有只有一个参数的类型构造函数才能成为函子。类型构造函数 StructureA 根本没有类型参数,因此不能是函子。