数据实例中的上下文
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]
我有一个数据类型,只有在它的参数可以被排序时才有意义,但是我似乎需要深入研究一些复杂且可能有问题的东西才能让它工作(主要是 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]