haskell - 为参数类型实现 Functor

标签 haskell functional-programming functor

有这种类型:

{-# LANGUAGE GADTs #-}

data Rgb a = (Num a, Show a) => Rgb a a a

我完全能够实现 Show 类型类:

instance Show (Rgb a) where
  show (Rgb r g b) = "Rgb (" ++ show r ++ "," ++ show g ++ "," ++ show b ++ ")"

但是如果我尝试对Functor做同样的事情:

instance Functor (Rgb a) where
  fmap f (Rgb r g b) = Rgb (f r) (f g) (f b)

我在 GHCi REPL 上得到以下输出:

<interactive>;:1093:19:
  The first argument of ‘Functor’ should have kind ‘* > *’,
    but ‘Rgb a’ has kind ‘*’
  In the instance declaration for ‘Functor (Rgb a)’

我当然会对解决方案和解释感到满意,而且还会有一个加深与这个问题相关的理论的链接。

为了解决这个问题,我(暂时)编写了这个函数:

mapRgb :: (Num a, Num b, Show a, Show b) => (a -> b) -> Rgb a -> Rgb b
mapRgb f (Rgb r g b) = Rgb (f r) (f g) (f b)

但我真的更喜欢为 Rgb 类型实现 fmap

最佳答案

您的 Functor 实例不应具有类型参数:

instance Functor Rgb where
  fmap f (Rgb r g b) = Rgb (f r) (f g) (f b)

如果您想派生实例,包括 Functor,请使用 DeriveFunctor 编译指示:

{-# LANGUAGE DeriveFunctor #-}

data Rgb a = Rgb a a a                   -- NOTE: DO NOT CONSTRAIN DATA!
    deriving (Show, Eq, Ord, Functor)

此外,数据类型声明的类型约束几乎总是无用的。约束需要这些约束的函数

<小时/>

您发现的问题是由于类型的类型造成的:种类。我们用 * 编写种类,GHCi 中的 :kind 命令可以提供帮助:

λ> :kind Int
Int :: *
λ> :kind Char
Char :: *
λ> :kind Maybe Int
Maybe Int :: *

所有仿函数都采用类型参数,因此它们都看起来像这样:

λ> :kind Maybe
Maybe :: * -> *
λ> :kind IO
IO :: * -> *

RGB 属于 * -> * 类型,但是当您编写 RGB a 时,您会应用 a::* 到它,它就变成了 RGB a::*,这对编译器来说没有意义。

现在您应该明白了:

The first argument of ‘Functor’ should have kind ‘* > *’,
  but ‘Rgb a’ has kind ‘*’
<小时/>

之前当您尝试实现仿函数实例时失败的原因是由于数据类型的这些限制:

-- Do not do this. This is poor Haskell.
data Rgb a = (Num a, Show a) => Rgb a a a

你应该写:

data Rgb a = Rgb a a a

然后在每个实例上添加约束:

instance (Show a) => Show (RGB a) where
    ...

instance (Num a) => Num (RGB a) where
    ...

然后你的仿函数实例就可以了。

关于haskell - 为参数类型实现 Functor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32681585/

相关文章:

haskell - 如何使用持久模型中的字段作为路由文件中的路径片段?

programming-languages - 部分评估和柯里化(Currying)

c++ - C++中的委托(delegate)是什么意思?

在 Boost 模板参数中发现 C++ 奇怪的语法

haskell - 在Haskell中连接列表中元组的第二个元素

haskell - 我可以将列表范围作为函数应用吗?

python - Haskell 中的缩进和 Python 中的一样吗?

scala - Bi 的名称 - 具有一个逆变参数和一个协变参数的仿函数类型类

haskell - 您如何识别一元设计模式?

c++ - Functor 类在构造函数中工作