簡體   English   中英

GHC無法推斷存在GADT和類型家族的類型

[英]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 +類型族”的存在下起作用。 在這方面,我有兩個問題:

  1. 為什么類型推斷不適用於上面的例子(我使用的是GHC7)?
  2. 什么是涉及GADTs和類型的功能(如一個非平凡例子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

好像你不介意VecNat索引,它是HM型,一切都應該簡單。

然后我們可以寫出nmp約束:

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') }))

使用功能時,即使是更智能的編譯器,也可以使用PlusPlus'統一plus的匿名定義。


值得從一篇更簡單的論文中獲取建議: FPH:Haskell的一流多態 ,特別是對於頂級定義:

  • 如果type是HM,則可以推斷出
  • 其他類型(RankN)應注釋,不能推斷。

至於非平凡的例子,我猜不可能有一個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.

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