[英]GHC could not infer types in presence of GADTs and Type Families
我有一個簡單的長度索引向量類型和一個append
長度索引向量的函數:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
module LengthIndexedList where
data Zero
data Succ a
type family Plus (a :: *) (b :: *) :: *
type instance Plus Zero b = b
type instance Plus (Succ a) b = Succ (Plus a b)
data Vec :: * -> * -> * where
VNil :: Vec a Zero
VCons :: a -> Vec a n -> Vec a (Succ n)
-- If you remove the following type annotation, type inference
-- fails.
-- append :: Vec a n1 -> Vec a n2 -> Vec a (Plus n1 n2)
append v1 v2 = case v1 of
VNil -> v2
(VCons x xs) -> VCons x (append xs v2)
編譯失敗,因為GHC無法推斷append
函數的類型。 我知道類型推斷在GADT和類型系列存在的情況下很棘手,部分原因在於多態遞歸。 然而,根據Vytiniotis等人的JFP論文, GHC7的類型推斷應該在“類型類+ GADT +類型族”的存在下起作用。 在這方面,我有兩個問題:
append
上面),其GHC可以推斷類型? 我沒有閱讀過一篇文章,這完全超出了我的想法,但我相信這個問題幾乎肯定是由類型家族引起的。 你有一個類型的功能
Vec a n1 -> Vec a n2 -> Vec a (Plus n1 n2)
但原則上,類型推斷無法識別。 我可以為你的代碼添加第二個類型的系列,
type family Plus' (a :: *) (b :: *) :: *
type instance Plus' Zero b = b
type instance Plus' (Succ a) b = Succ (Plus' a b)
看起來就像Plus
但名字不同。 推理無法弄清楚你是想要Plus
還是Plus'
。 推論永遠不會選擇,永遠不會讓自己進入一個可能必須選擇的位置(沒有像IncoherentInstances
這樣的一些非常不愉快的事情)。 因此,只有在沒有Plus
存在的情況下它才有效,推理才有效。 我對類型檢查背后的理論知之甚少,但我不認為類型族可以無處推斷。
我相信論文的意思是推理在所有這些事物存在的情況下仍然有用,並且在沒有它們的情況下仍然保持良好狀態。 例如,您可以編寫使用 append
函數且沒有類型簽名的代碼:
append3 a b c = a `append` b `append` c
額外獎勵注意事項: DataKinds
和封閉類型系列使一些代碼更容易理解。 我會寫這樣的代碼:
data Nat = Zero | Succ Nat
type family Plus (a :: Nat) (b :: Nat) :: Nat where
Plus Zero b = b
Plus (Succ a) b = Succ (Plus a b)
data Vec :: * -> Nat -> * where
VNil :: Vec a Zero
VCons :: a -> Vec a n -> Vec a (Succ n)
假設我們有以下定義:
append VNil v2 = v2
append (VCons x xs) v2 = VCons x (append xs v2)
從定義中可以明顯看出:
append :: Vec a n -> Vec a m -> Vec a p
好像你不介意Vec
的Nat
索引,它是HM型,一切都應該簡單。
然后我們可以寫出n
, m
和p
約束:
appendIndex Zero m ~ m -- from VNil case
appendIndex (Succ n) m ~ Succ (appendIndex n m) -- from VCons case
我沒有讀過JFP論文,但我認為OutsideIn無法解決這個問題。 它必須能夠在沒有任何上下文的情況下解決它們,即知道某處是Plus
。
它可以解決像(pseudosyntax,type lambda)這樣的約束:
append :: Vec a n -> Vec a m -> Vec a (rec f (λ n → case n of { Zero -> m ; Succ n' -> Succ (f n') }))
使用功能時,即使是更智能的編譯器,也可以使用Plus
或Plus'
統一plus的匿名定義。
值得從一篇更簡單的論文中獲取建議: FPH:Haskell的一流多態 ,特別是對於頂級定義:
至於非平凡的例子,我猜不可能有一個GHC類型語言沒有(甚至非遞歸)匿名類型函數(AFAIK)。
即使是非常簡單的(非遞歸類型)示例也會失敗
data NonEmpty :: * -> Bool -> * where
VNil :: NonEmpty a False
VCons :: a -> NonEmpty a b -> NonEmpty a True
append VNil v2 = v2
append (VCons x xs) v2 = VCons x (append xs v2)
因為它必須推斷
appendIndex True b = True
appendIndex False b = b
這本質上是||
在類型級別上。 GHC不支持(還有?)功能推廣。 所以你甚至不能寫
append :: NonEmpty a x -> NonEmpty b y -> NonEmpty b (x '|| y)
但是有可能實現這一目標http://www.cis.upenn.edu/~eir/papers/2014/promotion/promotion.pdf
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.