Haskell 创建具有类型约束的函子

Ste*_*ill 1 haskell functor

我有这个 Haskell 代码片段:

\n
{-# LANGUAGE InstanceSigs #-}\n\nmodule LatticePoint \nwhere\n\nimport Data.List    \n\ndata (Eq v) => LatticePoint v = LatticePoint{prob::Double, value::v}\n\ninstance Functor LatticePoint where\n    fmap :: (Eq a, Eq b) => (a -> b) -> LatticePoint a -> LatticePoint b \n    fmap f lp = LatticePoint {prob = prob lp, value = f $ value lp}\n
Run Code Online (Sandbox Code Playgroud)\n

编译时出现以下错误,我不明白

\n
src/LatticePoint.hs:12:14: error:\n    \xe2\x80\xa2 No instance for (Eq a)\n      Possible fix:\n        add (Eq a) to the context of\n          the type signature for:\n            fmap :: forall a b. (a -> b) -> LatticePoint a -> LatticePoint b\n    \xe2\x80\xa2 When checking that instance signature for \xe2\x80\x98fmap\xe2\x80\x99\n        is more general than its signature in the class\n        Instance sig: forall a b.\n                      (Eq a, Eq b) =>\n                      (a -> b) -> LatticePoint a -> LatticePoint b\n           Class sig: forall a b.\n                      (a -> b) -> LatticePoint a -> LatticePoint b\n      In the instance declaration for \xe2\x80\x98Functor LatticePoint\xe2\x80\x99\n   |\n12 |     fmap ::  (Eq a, Eq b) => (a -> b) -> LatticePoint a -> LatticePoint b \n   | \n\n         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n
Run Code Online (Sandbox Code Playgroud)\n

我想要实现的是将 LatticePoint 限制为 Eq 实例的任何类型 v,并使 LatticePoint 成为 Functor 的实例。

\n

Sil*_*olo 5

你的LatticePoint不是一个Functor. AFunctor被定义为“对于对类型ab,我可以将一个函数提升a -> b为一个函数f a -> f b”。您无法提供仅适用于几种类型或仅适用于某些类型但并非全部类型的实例。Set这与它不是函子的原因相同,因为它的所有重要操作都需要Ord元素类型。

在我们使用 的替代方案之前Functor,我们实际上需要从这篇文章的评论部分获取一些建议。正如所指出的,在 Haskell 中,通常不约束数据类型本身,而只是约束作用于它的所有函数(允许数据类型约束的功能在很大程度上被认为是错误功能,并且不鼓励使用)。因此,与其将Eq约束放在data行中,更惯用的做法是将该data行保留为普通声明并约束每个接受或生成LatticePoint.

这是因为,如果您直接限制数据类型,则甚至说for every都没有意义,这使得推理类型变得更加困难(每次您想在任何上下文中提及您的类型时,您实际上都需要在其后面加上一个正确性证明)。但是,如果每个类型都定义良好,但碰巧对其中某些类型来说是无用且不可构造的,那么推理我们的类型就会容易得多。所以我假设我们的声明是LatticePoint aaLatticePoint aa

data LatticePoint v = LatticePoint{prob::Double, value::v}
Run Code Online (Sandbox Code Playgroud)

我们可以用 来近似您想要的MonoFunctorMonoFunctor提供了更弱的约束。它说“给定我们容器的‘元素类型’的一些定义,我可以将元素上的函数提升为容器类型上的函数”。至关重要的是,我们从未说过它必须适用于所有类型,而只是适用于容器认为是有效“元素”的类型。

type instance Element (LatticePoint a) = a
Run Code Online (Sandbox Code Playgroud)

现在我们可以编写我们的MonoFunctor实例了。

instance Eq a => MonoFunctor (LatticePoint a) where
  omap :: (a -> a) -> LatticePoint a -> LatticePoint a
  omap f latticePoint = ...
Run Code Online (Sandbox Code Playgroud)

您会在这里注意到一件事:我们的函数必须映射从aa。它无法映射到不同的目标类型,即使源和目标都是Eq. 这只是 的限制MonoFunctor,而且我不知道有任何类型类允许MonoFunctor-style 类约束允许不是自同态的映射。