【问题标题】:Implementing Functor for a parametric type为参数类型实现 Functor
【发布时间】:2017-07-23 21:22:18
【问题描述】:

有这种类型:

{-# LANGUAGE GADTs #-}

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

我完全能够实现Show typeclass:

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

【问题讨论】:

    标签: haskell functional-programming functor


    【解决方案1】:

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

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

    如果您想派生实例,包括Functor,请使用DeriveFunctor pragma:

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

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


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

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

    所有Functors 都带有一个类型参数,所以它们都看起来像这样:

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

    RGB类似于* -&gt; *,但是当你写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
        ...
    

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

    【讨论】:

    • 您是否在 GHCi 中尝试过您的第一个提案?我也尝试过删除类型参数,但编译器仍然拒绝接受更改(老实说,我在发布问题之前也尝试过)。 :(
    • @gsscoder 这是因为您将数据类型中的类型限制为NumShow。这是一件坏事:您错误地使用了约束。 This 可能会解释原因,但现在,删除数据声明中的约束。我会将其添加到答案中。
    • 是的,现在我明白了,删除约束它有效;但我认为DeriveFunctor 是一条更简单的路径。
    • @gsscoder 当然可以,但我建议使用类型类,至少作为未来的练习。
    • 这真的是数据类型上下文吗?这个职位看起来更像是一个存在主义。数据类型上下文不是更靠左吗?
    猜你喜欢
    • 1970-01-01
    • 2020-02-20
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多