【问题标题】:How do you write a Functor on a data type that uses concrete types?如何在使用具体类型的数据类型上编写 Functor?
【发布时间】:2021-10-15 19:51:17
【问题描述】:

我正试图从 GO 世界进入 Haskell,所以几乎可以肯定我错过了一些微不足道的东西,但这让我很难过!

以下编译但没有任何用处。

data Thing a = Type1 Int | Type2 Int Int deriving Show

instance Functor Thing where
    fmap func (Type1 a) = Type1 a
    fmap func (Type2 a b) = Type2 a b

一旦我尝试实际使用“func”,它就无法编译。例如,只需在两个模式规则的第一个中使用“func”,就像这样..

data Thing a = Type1 Int | Type2 Int Int deriving Show

instance Functor Thing where
    fmap func (Type1 a) = Type1 (func a)
    fmap func (Type2 a b) = Type2 a b

给出以下错误,但如果我使用“Type1 a | Type2 a a”声明“Thing”,那么它可以编译。

x2.hs:4:34:
    Couldn't match expected type ‘Int’ with actual type ‘b’
      ‘b’ is a rigid type variable bound by
          the type signature for fmap :: (a -> b) -> Thing a -> Thing b
          at x2.hs:4:5
    Relevant bindings include
      func :: a -> b (bound at x2.hs:4:10)
      fmap :: (a -> b) -> Thing a -> Thing b (bound at x2.hs:4:5)
    In the first argument of ‘Type1’, namely ‘(func a)’
    In the expression: Type1 (func a)

x2.hs:4:39:
    Couldn't match expected type ‘a’ with actual type ‘Int’
      ‘a’ is a rigid type variable bound by
          the type signature for fmap :: (a -> b) -> Thing a -> Thing b
          at x2.hs:4:5
    Relevant bindings include
      func :: a -> b (bound at x2.hs:4:10)
      fmap :: (a -> b) -> Thing a -> Thing b (bound at x2.hs:4:5)
    In the first argument of ‘func’, namely ‘a’
    In the first argument of ‘Type1’, namely ‘(func a)’

谁能解释为什么以及我可以做些什么来解决它?是否可以在数据定义中使用具体类型,然后在 Functor 中使用它,或者我们是否仅限于使用泛型类型变量(如果我在这里使用了错误的词,请原谅)?

【问题讨论】:

  • fmap 必须与您作为参数传递的 any 函数一起使用,但您的定义仅适用于 Int -> Int 类型的函数。 Thing 有正确的 kind 作为函子,但这还不够。
  • data Thing a = ... 通常意味着 a 出现在 RHS 上。这就是Functor/fmap 所期待的。
  • 哇!那很快-谢谢。那么,我有这个权利吗.. 暗示 Functor 的数据类型(在这种情况下是事物)只能使用“通用”类型(这是正确的词),即:我不能具体说明嵌入的数据类型它是“容器”类型(在这种情况下为 Type1 和 Type2)。还是有其他方法可以做到这一点?否则,我们似乎失去了 Haskell 在这种用例中著名的严格类型控制。
  • 有一个mono-traversable 包可以让你将data Thing = Type1 Int | Type2 Int Int 之类的东西变成类似函子的东西,允许omap foo (Type1 x)omap foo (Type2 x y),只有在foo 没有类型 Int -> Int
  • F 变成Functor 的整个要点 是您可以fmap 任何类型a -> b 的任何函数,并且生成的函数将将F a 带到F b。这意味着在Functor F 实例定义中,您只会将映射函数应用于a 内部的aF,而不是任何其他值。如果你想做别的事情,那也不错,也不错,只是和Functorfmap无关。

标签: haskell functor


【解决方案1】:

是否可以在数据定义中使用具体类型,然后在 Functor 中使用它,或者我们是否仅限于使用泛型类型变量(如果我在这里使用了错误的词,请原谅)?

您不受限制。但是,只有类型是(或提及)数据声明中最后一个泛型变量的字段才能在您的 Functor 实例中进行修改。 (类型检查器允许对具有其他类型的字段进行一些修改;但所有这些都会导致违反函子定律。)因此,例如,它混合了通用字段和具体字段:

data Thing a = Type1 Int a | Type2 Int Int

instance Functor Thing where
    fmap f (Type1 n a) = Type1 n (f a)
    fmap f (Type2 m n) = Type2 m n

当然,没有什么能阻止你编写另一个函数,而不是命名为fmap,它可以随心所欲。所以,这也很好:

data Thing = Type1 Int | Type2 Int Int

notFmap :: (Int -> Int) -> Thing -> Thing
notFmap f (Type1 n) = Type1 (f n)
notFmap f (Type2 m n) = Type2 m n

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-04
    • 2016-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多