AbelianGroup g where gplus :: g -> g -> g gne-6ren">
gpt4 book ai didi

haskell "Could not deduce..."

转载 作者:行者123 更新时间:2023-12-05 04:09:31 27 4
gpt4 key购买 nike

我是 Haskell 的初学者,正在实现一些基本的代数,比如

class (Eq g) => AbelianGroup g where
gplus :: g -> g -> g
gnegate :: g -> g
gzero :: g
gminus :: g -> g -> g
gminus a b = gplus a (gnegate b)
gmult :: Integer -> g -> g

class (AbelianGroup d) => IntegralDomain d where
idtimes :: d -> d -> d
idone :: d

{- you can ignore upper code, it is added only for sake of completeness -}

class (IntegralDomain d) => EuclideanDomain d where
edeg :: d -> Integer
edivision :: d -> d -> (d, d)
egcd :: d -> d -> d
egcd f g | g == gzero = f
| f == gzero = g
| otherwise = let (q, r) = edivision f g in egcd g r

class (IntegralDomain f) => Field f where
finvert :: f -> f
fdivide :: f -> f -> f
fdivide a b = idtimes a (finvert b)

instance (Field f, IntegralDomain f) => EuclideanDomain f where
edeg x = 0
edivision x y = (fdivide x y, gzero)

当我得到错误时

* Could not deduce (Field d) arising from a use of `edivision'
from the context: EuclideanDomain d
bound by the class declaration for `EuclideanDomain'
at ...
Possible fix:
add (Field d) to the context of
the class declaration for `EuclideanDomain'
* In the expression: edivision f g
In a pattern binding: (q, r) = edivision f g
In the expression: let (q, r) = edivision f g in egcd g r

错误来自哪里——如错误陈述中一样——来自“EuclideanDomain”中“egcd”的默认定义中的“edivision f g”。所以作为 Haskell 的新手,我的问题是

Why is there this error?

如果我把 (Field d) 放在 EuclideanDomain 的声明中,这个错误就会消失。但是当然,整个代码变得毫无用处(不是每个欧几里得域都是域等)

最佳答案

tl;dr 您可能不想做您正在尝试做的事情。它在类型系统中造成了太多的复杂性。简单的解决方案就是不要编写与您尝试编写的实例一样通用的实例。

长版

instance (Field f, IntegralDomain f) => EuclideanDomain f where

你不想这样做。我不相信 Haskell 甚至不会让你在默认情况下这样做(你可能已经打开了一些编译器扩展来让它在某个时候工作)。您在这里所说的是“按照这样说的方式,每个域都是欧几里得域”。如果有人出现并制造了一种新类型 Foo , 他们可能想为 EuclideanDomain 定义一个实例和 Field , 但如果他们这样做了,那么就会有两个 EuclideanDomain 的实例对于同一类型。

这是一个问题。你可以告诉 GHC 忽略这个问题,只希望 OverlappingInstances 能解决问题。编译器扩展,但正如我所说,您可能不想这样做,因为它会使您的代码更加困惑。您可能还需要 FlexibleInstances也许还有其他一些获得通用的类型类实例以通过类型检查器。现在,您有两个选择。

选项1

让熟睡的狗撒谎,并假设用户足够聪明,可以同时实现这两个 FieldEuclideanDomain .如果你想这样做,你可以通过提供“默认”功能来简化他们,如果他们真的想派生 EuclideanDomain,他们可以将这些功能复制到他们的实例声明中。来自 Field .

edegDefault :: Field f => f -> Integer
edegDefault x = 0
edivisionDefault :: Field f => f -> f -> (f, f)
edivisionDefault x y = (fdivide x y, gzero)

然后用户可以执行EuclideanDomain他们自愿的。如果他们想自己实现功能,可以自由实现,但如果他们不想实现,也可以随时实现。

instance EuclideanDomain f where
edeg = edegDefault
edivision = edivisionDefault

选项 2

您不时看到的另一个选项更适用于用户可能真正忘记实现 EuclideanDomain 的情况。 .在这种情况下,我们会包装我们的 Field newtype 中的实例并声明 newtype成为EuclideanDomain .

newtype WrappedField f = WrappedField f

instance Field f => EuclideanDomain (WrappedField f) where
...

然后您仍然可以在不侵入用户实例空间的情况下获得功能。在 Haskell 标准库中可以看到这种模式。最长的时间,Monad不是 Applicative 的子类由于历史原因,即使在数学上它应该是。因此,Haskell 语言设计者实现了一个 WrappedMonad采取 Monad 的新型实例并提供了Applicative实例。

关于 haskell "Could not deduce...",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45827586/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com