Haskell 数据实例中的上下文
我有一个数据类型,它只有在其参数可以排序的情况下才有意义,但是我似乎需要深入研究一些复杂的、可能有黑客攻击的东西,才能让它工作(主要是GADTs)。 我所做的(受约束的数据类型)是否被认为是糟糕的haskell实践,是否有办法解决这个问题 对于感兴趣的人,以下是相关代码:Haskell 数据实例中的上下文,haskell,type-constraints,gadt,Haskell,Type Constraints,Gadt,我有一个数据类型,它只有在其参数可以排序的情况下才有意义,但是我似乎需要深入研究一些复杂的、可能有黑客攻击的东西,才能让它工作(主要是GADTs)。 我所做的(受约束的数据类型)是否被认为是糟糕的haskell实践,是否有办法解决这个问题 对于感兴趣的人,以下是相关代码: {-# LANGUAGE GADTs #-} {-# LANGUAGE InstanceSigs #-} import Data.List (sort) data OrdTriple a where OrdTripl
{-# 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
上的约束,即其返回类型是可排序的
实际上,我得到了以下编译错误,这似乎是因为我过度约束了函子实例:
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
类来执行此操作,因为它的fmap
必须在没有约束的情况下对所有数据类型都有效
你可以和另一个班一起工作。一个选项是使用“细粒度函子”类,它允许您为每对类型ab
使用单独的实例。(可能这已经有了一些标准名称,但我不记得了)
或者,可以使用约束参数化函子
类:
{-# 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]
Functor必须适用于所有类型,而不仅仅是受
Ord
约束的类型。即使在实例中,也无法进一步约束它。这就是为什么像平衡树这样的结构也不能是函子。你不能把它变成函子。通常的解决方法是简单地实现一个单独的“映射”函数,就像对Data.Set
和Data.map
所做的那样。您的IxFunctor是否与or相关(略有不同)?我不知道它们是否以某种我看不到的方式代表了类似的概念,或者它们是否完全不同。@Zoeyhewl似乎我弄错了术语:上面的一个不是索引函子fija
,而是其他东西。我记不起它的真名了,但如果在什么地方找不到它,我会很惊讶。我将删去这些术语,以免引起混淆。谢谢。对于CFunctor,我得到的不在范围内:类型构造函数或类“约束”
@zoeyHew将看到我的编辑,我添加了导入并对类进行了一些修改,因为我还意识到我们需要函数依赖项或类型族。我选择后者。
{-# 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]