[英]GHC cannot deduce (a1 ~ a) with GADTs and existential types
我的代碼無法編譯:
{-# 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
我得到的錯誤是:
Could not deduce (a1 ~ a)
[...]
Expected type: a -> a -> a -> Bool
Actual type: a1 -> a1 -> a1 -> Bool
In the expression: f'
[...]
對於ellipsoid
和halfplane
兩種功能。
我不明白,我正在尋找一個答案,為什么a
不能等同於a1
,兩者都是RealFrac
,甚至更好:為什么推導出兩種不同的類型( a ~ a1
)?
你使用GADT
和隱含的forall
會讓你感到悲傷。 現在是提及“類型化存在量化” 反模式的好時機
既然你已經包括了約束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 => ...
這同樣適用於TestFunc
:
type TestFunc = forall a. RealFrac a => a -> a -> a -> Bool
這就是GHC強迫您添加RankNTypes
擴展的原因。
由於forall
的a
在構造函數PointSet
不可能用統一a
在TestFunc
因為a
在PointSet
是一些具體的實例RealFrac
,但TestFunc
是需要為任何一個工作功能a
。
特定類型a
和普遍量化的forall a. a
之間的這種差異forall a. a
forall a. a
導致GHC推導出兩種不同的類型a
和a1
。
解決方案? 廢棄所有這些存在的廢話。 在需要的地方和時間應用類型類約束:
{-# 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' = ...
現在PointSet
有兩個參數:一個幻影類型s :: Shape
,它有種類Shape
(種類是類型的類型)和a
與你的原始例子相同,除了作為PointSet
的顯式參數,它不再是隱式存在量化的。
為了解決您的最后一點,在a
和a1
錯誤消息都沒有“均為RealFrac
。 RealFrac
不是一個類型,它是一個類型類。 a
和a1
是兩個潛在的不同類型,無論碰巧的實例RealFrac
。
也就是說 ,除非你使用更具表現力的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.