這是棘手的,但這樣做,能夠與某些類型的水平的黑客。可悲的是它比你想要的更復雜。
我避免封閉式的家庭因此這將GHC 7.6編譯和高達
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Proxy
data Nat = S Nat | Z -- Type level natural numbers, Z means zero, S n means n + 1
type family IteratedList (n :: Nat) a
type instance IteratedList Z a = [a] -- Base case
type instance IteratedList (S n) a = [IteratedList n a] -- Inductive step
注意,這個工程完全一樣數學歸納法?我們「證明」什麼IteratedList
是當n
是Z
,然後我們可以「證明」n
是S n
使用IteratedList n a
時它是什麼。
這實際上可以採取相當深層次的,我們很快開始撞到差GHC的開放型家庭的範圍,並最終需要GHC 7.8。
爲了幫助GHC的類型檢查有了這一切,我們需要提供所謂Proxy
秒。這些只是簡單的數據類型,我們用一個幻像類型變量來表示類型檢查器的有用內容。
下面是我們稍後使用的幫助函數,其中Proxy
包含一個大於零的數字,並返回一個數字減1的代理。
predProxy :: Proxy (S n) -> Proxy n
predProxy = reproxy
現在我們使用類型類跨 IteratedList
小號推廣業務。這些類基本上像通過歸納一樣工作。 例如,以下是在IteratedList
內製作單個元素的類。
class Single (n :: Nat) a where
single :: Proxy n -> a -> IteratedList n a
instance Single Z a where
single _ = (:[])
instance Single n a => Single (S n) a where
single p a = [single (predProxy p) a]
需要的Proxy n
參數來幫助GHC的類型檢查明白這是怎麼回事,因爲否則會產生很多新的類型變量,然後固執地不統一它們。據推測,這有一個聰明的原因。
請注意,類型類的兩個實例看起來非常類似於我們如何定義IteratedList
?一個用於Z
,然後是一個歸納步驟。
至於在運行時構造這個..從技術上講,這全部都是基於類型同義詞,所以你實際上並不需要一個轉換例程,只是爲了構建列表。棘手的一點是,你需要將這個n
放在存儲器或CPS代碼中,這樣你就可以有1個返回類型。
否則你的返回類型將取決於你的輸入,你不能表達一些如此複雜的GHC。
你能想象像
buildIteratedList :: String -> (forall n. IteratedList n a -> r) -> r
但很快就變得相當可怕以來每操作需要是一個類型類感應GOOP,以便在n
要普及工作。
我建議只是做一些簡單的像
data Nested a = Elem a
| List [Nested a]
,做運行時檢查..如果沒有相關的類型,這似乎很不可行了我。
是否希望解析結果的類型取決於解析的字符串?您需要一種具有相關類型的語言來實現這一點。 Haskell不是其中之一。請注意,你並不需要它。 –
爲什麼你想在類型級別做到這一點? –
您將無法編寫解析器,而解析器的類型取決於被解析的內容,Haskell的類型系統無法正常工作。您必須提前知道該類型。 – Dan