我有这个Haskell代码片段:
{-# LANGUAGE InstanceSigs #-}
module LatticePoint
where
import Data.List
data (Eq v) => LatticePoint v = LatticePoint{prob::Double, value::v}
instance Functor LatticePoint where
fmap :: (Eq a, Eq b) => (a -> b) -> LatticePoint a -> LatticePoint b
fmap f lp = LatticePoint {prob = prob lp, value = f $ value lp}
在编译时,我得到以下错误,我不理解
src/LatticePoint.hs:12:14: error:
• No instance for (Eq a)
Possible fix:
add (Eq a) to the context of
the type signature for:
fmap :: forall a b. (a -> b) -> LatticePoint a -> LatticePoint b
• When checking that instance signature for ‘fmap’
is more general than its signature in the class
Instance sig: forall a b.
(Eq a, Eq b) =>
(a -> b) -> LatticePoint a -> LatticePoint b
Class sig: forall a b.
(a -> b) -> LatticePoint a -> LatticePoint b
In the instance declaration for ‘Functor LatticePoint’
|
12 | fmap :: (Eq a, Eq b) => (a -> b) -> LatticePoint a -> LatticePoint b
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
我想要实现的是将LatticePoint限制为任何类型v,这是Eq的实例,并使LatticePoint成为Functor的实例。
您的LatticePoint
不是Functor
。一个Functor
被定义为声明"对于每一对类型a
和b
,我可以将函数a -> b
提升为函数f a -> f b
"。您不能提供只适用于少数类型的实例,也不能提供只适用于某些类型而不是全部类型的实例。这与Set
不是函子的原因相同,因为对其进行的所有重要操作都需要对元素类型进行Ord
操作。
在我们可以使用Functor
的替代品之前,我们实际上需要从这篇文章的评论部分获得一些建议。正如所指出的,在Haskell中,典型的做法是不约束数据类型本身,而仅仅约束作用于它的所有函数(允许数据类型约束的特性在很大程度上被认为是一个错误的特性,不鼓励使用)。因此,与其将Eq
约束放在data
行,不如将data
行保留为普通声明,并约束每个接受或产生LatticePoint
的函数,这更习惯。
这是因为,如果您直接约束数据类型,对于每个a
说LatticePoint a
甚至没有意义,这使得对类型进行推理变得更加困难(每次您想在任何上下文中提到您的类型时,您都需要有效地伴随着正确性证明)。但是,如果LatticePoint a
类型对于每个a
都是定义良好的,但恰好对其中一些是无用的且不可构造的,则更容易推断我们的类型。所以我假设我们的声明是
data LatticePoint v = LatticePoint{prob::Double, value::v}
我们可以用MonoFunctor
近似你想要的。MonoFunctor
提供了一个弱得多的约束。它说"给定容器的元素类型的定义,我可以将元素上的函数提升到容器类型上的函数"。重要的是,我们从来没有说过它必须适用于所有类型,只适用于容器说有效的"元素"类型。
type instance Element (LatticePoint a) = a
现在我们可以编写MonoFunctor
实例了。
instance Eq a => MonoFunctor (LatticePoint a) where
omap :: (a -> a) -> LatticePoint a -> LatticePoint a
omap f latticePoint = ...
你会注意到一件事:我们的函数必须从a
映射到a
。它不能映射到不同的目标类型,即使源和目标都是Eq
。这只是MonoFunctor
的一个限制,我不知道有任何类型类允许MonoFunctor
风格的类约束和允许非自同态的映射。
将我的注释转换为答案,您通常将约束推迟到在值需要Eq
约束的上下文中使用LatticePoint
的函数。这允许您定义fmap
。
module LatticePoint
where
import Data.List
data LatticePoint v = LatticePoint{prob::Double, value::v}
instance Functor LatticePoint where
fmap :: (a -> b) -> LatticePoint a -> LatticePoint b
fmap f lp = LatticePoint {prob = prob lp, value = f $ value lp}
foo :: Eq a => LatticePoint a -> Whatever
foo lp = ...
如果你仔细想想,数据结构本身并不关心value
是否可以进行相等比较,只有使用数据结构的函数。
作为一个具体的例子,考虑(==)
本身的定义:
instance Eq a => Eq (LatticePoint a) where
(LatticePoint p1 v1) == (LatticePoint p2 v2) = p1 == p2 && v1 == v2