[英]Can I get GHC to infer a constraint past a GADT pattern match?
我想讓 GHC 推斷出超過 GADT 模式匹配的約束。 例如,假設我有兩個表達式,每個表達式都有一個推斷約束:
f :: _ => a
g :: _ => a
(在我的用例中,這些推斷的約束可能很大,因此手工寫出它們是不可行的。)
然后假設我想使用f
或g
取決於 boolean 條件。 天真地,我可以進行如下操作:
h1 :: _ => Bool -> a
h1 c = if c then f else g
假設我調用f
ct_f
和g
ct_g
的推斷約束,那么 GHC 將推斷h1
的約束( ct_f, ct_g )
。
問題是這是一個過於嚴格的類型:如果 Boolean 是True
我不需要ct_g
,反之如果是False
我不需要ct_f
。 所以我嘗試使用標准機制來啟用這樣的依賴約束:
data SBool (c :: Bool) where
SFalse :: SBool False
STrue :: SBool True
h2 :: _ => SBool bool -> a
h2 = \case
STrue -> f
SFalse -> g
然而這不起作用,因為 GHC 的部分類型簽名算法拒絕浮動約束超過 GADT 模式匹配。 相反,我可以嘗試明確地告訴 GHC 該怎么做:
ifC :: forall ct_t ct_f bool x. SBool bool -> ( ct_t => x ) -> ( ct_f => x ) -> ( If bool ct_t ct_f => x )
ifC STrue a _ = a
ifC SFalse _ b = b
h3 :: _ => SBool bool -> a
h3 c = ifC c f g
這種方法也失敗了,這一次是因為 GHC 認為ifC
的類型簽名不明確,即 GHC 需要用戶顯式傳遞約束,例如
h4 c = ifC @ct_f @ct_g c f g
不幸的是,我無法明確傳遞這些約束:我要求 GHC 推斷它們,並且無法引用它們。 例如,可以嘗試將它們帶入 scope,如下所示:
h5 :: _ => SBool bool -> a
h5 c =
let
f :: _ct_f => a
f' = f
g :: _ct_g => a
g' = g
in
if_C @_ct_f @_ct_g c f' g'
但這行不通,因為 GHC 不支持命名通配符代替額外的約束(即使支持,它們也不會正確 scope)。
是否有另一種方法可以讓 GHC 推斷:
h :: ( If bool ct_f ct_g ) => a
我使用ImpredicativeTypes
稍微改進了代碼。
type family GetBool a where
GetBool (SBool True) = True
GetBool (SBool False) = False
data TF (a :: Constraint) x = TF x
class SIf a pt pf x where
ifC' :: a -> TF pt x -> TF pf x -> (If (GetBool a) pt pf => x)
instance ((t => x) ~ (f => x)) => SIf (SBool True) t f x where
ifC' _ (TF t) _ = t
instance ((t => x) ~ (f => x)) => SIf (SBool False) t f x where
ifC' _ _ (TF f) = f
h3' :: _ => SBool bool -> a
h3' c = ifC' c f g
它可以給它Num a
實例。
*Main> :t h3' 3
h3' 3
:: (If (GetBool (SBool bool)) pt pf, SIf (SBool bool) pt pf a,
Num (SBool bool)) =>
a
let x = h3' f
現在也可以,但並不完美。 我想我們做的是黑魔法...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.