【问题标题】:Context in data instances数据实例中的上下文
【发布时间】:2017-03-17 13:32:03
【问题描述】:

我有一个数据类型,只有在可以对其参数进行排序时才有意义,但是我似乎需要深入研究一些复杂且可能很麻烦的东西才能使其工作(主要是 GADT)。 我正在做的事情(受约束的数据类型)是否被认为是不良的 Haskell 做法,有什么办法可以解决这个问题吗?

有兴趣的可以看看相关代码:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
import Data.List (sort)

data OrdTriple a where
    OrdTriple :: (Ord a) => a -> a -> a -> OrdTriple a

instance Functor OrdTriple where
    fmap :: (Ord a, Ord b) => (a -> b) -> OrdTriple a -> OrdTriple b
    fmap f (OrdTriple n d x) = OrdTriple n' d' x'
        where 
            [n', d', x'] = sort [f n, f d, f x]

起初我以为我只是在 Functor 实例中放置一个上下文(这是我唯一苦苦挣扎的实例),但似乎我不能(没有提及包含的类型),即使我可以我仍然需要对 fmap 的约束,即它的返回类型是可订购的。

事实上,我收到以下编译错误,这似乎是因为我过度约束 Functor 实例:

No instance for (Ord a)
Possible fix:
  add (Ord a) to the context of
    the type signature for
      fmap :: (a -> b) -> OrdTriple a -> OrdTriple b
When checking that:
    forall a b.
    (Ord a, Ord b) =>
    (a -> b) -> OrdTriple a -> OrdTriple b
  is more polymorphic than:
    forall a b. (a -> b) -> OrdTriple a -> OrdTriple b
When checking that instance signature for ‘fmap’
  is more general than its signature in the class
  Instance sig: forall a b.
                (Ord a, Ord b) =>
                (a -> b) -> OrdTriple a -> OrdTriple b
     Class sig: forall a b. (a -> b) -> OrdTriple a -> OrdTriple b
In the instance declaration for ‘Functor OrdTriple’

【问题讨论】:

  • Functor 必须适用于所有类型,而不仅仅是那些受Ord 约束的类型。即使在您的实例中,您也无法进一步限制它。这就是为什么像平衡树这样的结构也不能成为函子的原因。
  • 你不能把它变成函子。通常的解决方法是简单地实现一个单独的“map”函数,就像 Data.SetData.Map 所做的那样。

标签: haskell type-constraints gadt


【解决方案1】:

您不能使用标准的Functor 类来做到这一点,因为它的fmap 必须适用于所有 数据类型,没有约束。

你可以和不同的班级一起工作。一种选择是使用“细粒度仿函数”类,它允许您为每对类型 a b 使用单独的实例。 (可能这已经有一些标准名称了,但我不记得了)

class FgFunctor f a b where
   fgmap :: (a->b) -> f a -> f b

-- regular functors are also fine-grained ones, e.g.
instance FgFunctor [] a b where
   fgmap = fmap

instance (Ord a, Ord b) => FgFunctor OrdTriple a b where
   fgmap f (OrdTriple n d x) = OrdTriple n' d' x'
      where [n', d', x'] = sort [f n, f d, f x]

或者,可以使用约束参数化Functor 类:

{-# LANGUAGE GADTs, KindSignatures, MultiParamTypeClasses, 
    ConstraintKinds, TypeFamilies, FlexibleInstances #-}
{-# OPTIONS -Wall #-}
module CFunctor where

import Data.List (sort)
import Data.Kind (Constraint)

data OrdTriple a where
    OrdTriple :: (Ord a) => a -> a -> a -> OrdTriple a

class CFunctor (f :: * -> *) where
   type C f a :: Constraint
   cmap :: (C f a, C f b) => (a -> b) -> f a -> f b

-- regular functors are also constrained ones, e.g.
instance CFunctor [] where
   type C [] a = a ~ a
   cmap = fmap

instance CFunctor OrdTriple where
   type C OrdTriple a = Ord a
   cmap f (OrdTriple n d x) = OrdTriple n' d' x'
      where [n', d', x'] = sort [f n, f d, f x]

【讨论】:

  • 您的 IxFunctor 是否与 this 或(略有不同)this 相关?我不知道它们是否以我没有看到的某种方式代表了相似的概念,或者它们是否完全不同。
  • @ZoeyHewll 看来我搞砸了术语:上面的不是索引函子f i j a,而是别的东西。我不记得它的正确名称,但如果在某个地方找不到它,我会感到惊讶。我将编辑掉术语,以免引起混淆。谢谢。
  • 对于 CFunctor,我收到了 Not in scope: type constructor or class ‘Constraint’
  • @ZoeyHewll 查看我的编辑,我添加了导入并对类进行了一些修改,因为我还意识到我们需要一个函数依赖或类型族。我选择了后者。
猜你喜欢
  • 2018-02-03
  • 2011-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
  • 1970-01-01
  • 2021-05-11
相关资源
最近更新 更多