Haskell 创建具有类型约束的仿函数
Haskell create functor with type constraints
我有这个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 限制为作为 Eq 实例的任何类型 v,并使 LatticePoint 成为 Functor 的实例。
您的 LatticePoint
不是 Functor
. A Functor
is defined to state "For every pair of types a
and b
, I can lift a function a -> b
to a function f a -> f b
". You can't provide an instance that only works for a few types, or for some but not all. This is the same reason Set
不是函子,因为对它的所有重要操作都需要 Ord
元素类型。
在我们可以使用替代 Functor
之前,我们实际上需要从这个 post 的评论部分获取一些建议。正如所指出的,在 Haskell 中,通常不约束数据类型本身,而仅约束作用于它的所有函数(允许数据类型约束的特性在很大程度上被认为是一种错误特性,不鼓励使用它)。因此,与其将 Eq
约束放在 data
行中,不如将 data
行保留为普通声明并约束每个采用或产生 LatticePoint
的函数更为惯用。
这是因为,如果您直接限制数据类型,甚至 对每个 a
说 LatticePoint a
都没有意义 ,这使得它变得更加困难对类型进行推理(每次您想在任何上下文中提及您的类型时,您都需要有效地证明其正确性)。但是如果 LatticePoint a
类型对于每个 a
都是 well-defined 但恰好对其中一些无用且不可构造,那么推理就容易多了关于我们的类型。所以我假设我们的声明是
data LatticePoint v = LatticePoint{prob::Double, value::v}
我们可以用 MonoFunctor
来近似您想要的。 MonoFunctor
提供了一个弱得多的约束。它说“给定我们容器的 'element type' 的一些定义,我可以将元素上的函数提升为容器类型上的函数”。至关重要的是,我们从未说过它必须适用于 所有 类型,仅适用于容器认为是有效“元素”的类型。
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
的限制,我不知道有任何类型 class 允许 MonoFunctor
样式 class 约束 和 允许不是自同态的映射。
将我的评论转换为答案,您通常会在值需要 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
我有这个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 限制为作为 Eq 实例的任何类型 v,并使 LatticePoint 成为 Functor 的实例。
您的 LatticePoint
不是 Functor
. A Functor
is defined to state "For every pair of types a
and b
, I can lift a function a -> b
to a function f a -> f b
". You can't provide an instance that only works for a few types, or for some but not all. This is the same reason Set
不是函子,因为对它的所有重要操作都需要 Ord
元素类型。
在我们可以使用替代 Functor
之前,我们实际上需要从这个 post 的评论部分获取一些建议。正如所指出的,在 Haskell 中,通常不约束数据类型本身,而仅约束作用于它的所有函数(允许数据类型约束的特性在很大程度上被认为是一种错误特性,不鼓励使用它)。因此,与其将 Eq
约束放在 data
行中,不如将 data
行保留为普通声明并约束每个采用或产生 LatticePoint
的函数更为惯用。
这是因为,如果您直接限制数据类型,甚至 对每个 a
说 LatticePoint a
都没有意义 ,这使得它变得更加困难对类型进行推理(每次您想在任何上下文中提及您的类型时,您都需要有效地证明其正确性)。但是如果 LatticePoint a
类型对于每个 a
都是 well-defined 但恰好对其中一些无用且不可构造,那么推理就容易多了关于我们的类型。所以我假设我们的声明是
data LatticePoint v = LatticePoint{prob::Double, value::v}
我们可以用 MonoFunctor
来近似您想要的。 MonoFunctor
提供了一个弱得多的约束。它说“给定我们容器的 'element type' 的一些定义,我可以将元素上的函数提升为容器类型上的函数”。至关重要的是,我们从未说过它必须适用于 所有 类型,仅适用于容器认为是有效“元素”的类型。
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
的限制,我不知道有任何类型 class 允许 MonoFunctor
样式 class 约束 和 允许不是自同态的映射。
将我的评论转换为答案,您通常会在值需要 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