数据实例中的上下文

Context in data instances

我有一个数据类型,只有在它的参数可以被排序时才有意义,但是我似乎需要深入研究一些复杂且可能有问题的东西才能让它工作(主要是 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 的约束,即它的 return 类型是可订购的。

事实上,我收到以下编译错误,这似乎是因为我过度限制了 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 class 执行此操作,因为它的 fmap 必须适用于 all 数据类型,没有约束。

您可以使用不同的 class。一种选择是使用 "fine-grained functor" class,它允许您为每对类型 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 class:

{-# 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]