簡體   English   中英

我可以讓 GHC 推斷出超過 GADT 模式匹配的約束嗎?

[英]Can I get GHC to infer a constraint past a GADT pattern match?

我想讓 GHC 推斷出超過 GADT 模式匹配的約束。 例如,假設我有兩個表達式,每個表達式都有一個推斷約束:

f :: _ => a
g :: _ => a

(在我的用例中,這些推斷的約束可能很大,因此手工寫出它們是不可行的。)

然后假設我想使用fg取決於 boolean 條件。 天真地,我可以進行如下操作:

h1 :: _ => Bool -> a
h1 c = if c then f else g

假設我調用f ct_fg 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM