[英]GHC cannot deduce (a1 ~ a) with GADTs and existential types
My code does not compile: 我的代码无法编译:
{-# LANGUAGE EmptyDataDecls, GADTs, RankNTypes #-}
import Data.Ratio
data Ellipsoid
data Halfplane
data PointSet a where
Halfplane :: RealFrac a => a -> a -> a -> (a -> a -> Bool) -> a -> PointSet Halfplane
Ellipsoid :: RealFrac a => a -> a -> a -> (a -> a -> Bool) -> a -> PointSet Ellipsoid
type TestFunc = RealFrac a => (a -> a -> a -> Bool)
ellipsoid :: PointSet Ellipsoid -> TestFunc
ellipsoid (Ellipsoid a b c f r) = f' where f' z y x = ((x/a)^2 + (y/b)^2 + (z/c)^2) `f` r
halfplane :: PointSet Halfplane -> TestFunc
halfplane (Halfplane a b c f t) = f' where f' z y x = (a*x + b*y + c*z) `f` t
The error I get is: 我得到的错误是:
Could not deduce (a1 ~ a)
[...]
Expected type: a -> a -> a -> Bool
Actual type: a1 -> a1 -> a1 -> Bool
In the expression: f'
[...]
for both functions ellipsoid
and halfplane
. 对于ellipsoid
和halfplane
两种功能。
I don't understand and am looking for an answer, why a
cannot be equated with a1
, both being RealFrac
, or even better: why two different types ( a ~ a1
) are deduced? 我不明白,我正在寻找一个答案,为什么a
不能等同于a1
,两者都是RealFrac
,甚至更好:为什么推导出两种不同的类型( a ~ a1
)?
Your use of GADT
with implicit forall
is causing you grief. 你使用GADT
和隐含的forall
会让你感到悲伤。 This is a good time to mention the "existential quantification with typeclass" anti-pattern 现在是提及“类型化存在量化” 反模式的好时机
Since you've included the constraint RealFrac a
in the definition of PointSet
you're implicitly using forall
like this: 既然你已经包括了约束RealFrac a
中的定义PointSet
你使用隐式forall
是这样的:
data PointSet a where
Halfplane :: forall a. RealFrac a => a -> a -> a -> (a -> a -> Bool) -> a -> PointSet HalfPlane
Ellipsoid :: forall a. RealFrac a => ...
The same applies to TestFunc
: 这同样适用于TestFunc
:
type TestFunc = forall a. RealFrac a => a -> a -> a -> Bool
This is why GHC coerced you into adding the RankNTypes
extension. 这就是GHC强迫您添加RankNTypes
扩展的原因。
Because of forall
the a
in the constructors for PointSet
cannot possibly unify with the a
in TestFunc
since the a
in PointSet
is some specific instance of RealFrac
, but TestFunc
is a function that is required to work for any a
. 由于forall
的a
在构造函数PointSet
不可能用统一a
在TestFunc
因为a
在PointSet
是一些具体的实例RealFrac
,但TestFunc
是需要为任何一个工作功能a
。
This difference between the specific type a
and the universally quantified forall a. a
特定类型a
和普遍量化的forall a. a
之间的这种差异forall a. a
forall a. a
causes GHC to deduce two different types a
and a1
. forall a. a
导致GHC推导出两种不同的类型a
和a1
。
The solution? 解决方案? Scrap all this existential nonsense. 废弃所有这些存在的废话。 Apply the typeclass constraints where and when they're needed : 在需要的地方和时间应用类型类约束:
{-# LANGUAGE DataKinds, GADTs, KindSignatures #-}
data Shape = Halfplane | Ellipsoid -- promoted via DataKinds
data PointSet (s :: Shape) a where
Halfplane :: a -> a -> a -> (a -> a -> Bool) -> a -> PointSet Halfplane a
Ellipsoid :: ...
type TestFunc a = a -> a -> a -> Bool
ellipsoid :: RealFrac a => PointSet Ellipsoid a -> TestFunc a
ellipsoid (Ellipsoid a b c f r) = f' where f' = ...
Now PointSet
takes 2 parameters: a phantom type s :: Shape
which has kind Shape
(kinds are the types of types) and a
which is the same as your original example, except as an explicit argument to PointSet
it is no longer implicitly existentially quantified. 现在PointSet
有两个参数:一个幻影类型s :: Shape
,它有种类Shape
(种类是类型的类型)和a
与你的原始例子相同,除了作为PointSet
的显式参数,它不再是隐式存在量化的。
To address your last point, the a
and a1
in the error message are not "both being RealFrac
. RealFrac
is not a type, it is a typeclass. a
and a1
are two potentially distinct types that both happen to be instances of RealFrac
. 为了解决您的最后一点,在a
和a1
错误消息都没有“均为RealFrac
。 RealFrac
不是一个类型,它是一个类型类。 a
和a1
是两个潜在的不同类型,无论碰巧的实例RealFrac
。
That said , unless you are making use of the more expressive type of PointSet
there is a much simpler solution. 也就是说 ,除非你使用更具表现力的PointSet
类型, PointSet
有一个更简单的解决方案。
data PointSet a
= Halfplane a a a (a -> a -> Bool) a
| Ellipsoid a a a (a -> a -> Bool) a
testFunc :: RealFrac a => PointSet a -> TestFunc a
testFunc (Ellipsoid a b c f r) = f' where f' = ...
testFunc (Halfplane a b c f t) = f' where f' = ...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.