假設列表L的長度爲n在列表J中交織,長度爲n + 1。 我們想知道,對於J的每個元素,L中的哪個鄰居是更大的。 下面的函數需L作爲其輸入,併產生一個列表K,也長度的 n + 1個,使得K的我個元素是J的我 th元素的期望鄰居使用類型系統來檢查輸出與輸入列表的長度
aux [] prev acc = prev:acc
aux (hd:tl) prev acc = aux tl hd ((max hd prev):acc)
expand row = reverse (aux row 0 [])
我可以證明我自己,非正式,這個函數(我 OCaml中最初寫)的結果的長度比輸入的長度更大之一。但我 跳轉到Haskell(一種新的語言對我來說),因爲我有興趣是 能夠通過類型系統證明這個不變量持有。隨着幫助 的this previous answer,我 能夠儘量得到如下所示:
{-# LANGUAGE GADTs, TypeOperators, TypeFamilies #-}
data Z
data S n
type family (:+:) a b :: *
type instance (:+:) Z n = n
type instance (:+:) (S m) n = S (m :+: n)
-- A List of length 'n' holding values of type 'a'
data List a n where
Nil :: List a Z
Cons :: a -> List a m -> List a (S m)
aux :: List a n -> a -> List a m -> List a (n :+: (S m))
aux Nil prev acc = Cons prev acc
aux (Cons hd tl) prev acc = aux tl hd (Cons (max hd prev) acc)
然而,在最後一行產生以下錯誤:
* Could not deduce: (m1 :+: S (S m)) ~ S (m1 :+: S m)
from the context: n ~ S m1
bound by a pattern with constructor:
Cons :: forall a m. a -> List a m -> List a (S m),
in an equation for `aux'
at pyramid.hs:23:6-15
Expected type: List a (n :+: S m)
Actual type: List a (m1 :+: S (S m))
* In the expression: aux tl hd (Cons (max hd prev) acc)
In an equation for `aux':
aux (Cons hd tl) prev acc = aux tl hd (Cons (max hd prev) acc)
* Relevant bindings include
acc :: List a m (bound at pyramid.hs:23:23)
tl :: List a m1 (bound at pyramid.hs:23:14)
aux :: List a n -> a -> List a m -> List a (n :+: S m)
(bound at pyramid.hs:22:1)
看來,我需要什麼做的是教編譯器,(x :+: (S y)) ~ S (x :+: y)
。這可能嗎?
另外,有沒有比類型系統更好的工具來解決這個問題?
它看起來有可能,但它似乎需要證明通過對'x'進行歸納,並且可能在某處定義和使用一個單獨的'x'。而且,這個機器在運行時不會被擦除,所以我們只浪費O(n)個步驟來產生一個'Refl'。 (我真的希望GHC將它優化到O(1)有一天...... :)) – chi
@chi你已經多次提到過這種GHC優化(我可以回憶),我真的開始希望它存在太。你知道這是否有任何現有的努力? – Alec
@Alec對於我長時間的咆哮/祝願,我很抱歉,我忍不住了:)我沒有跟隨GHC開發,所以我不能評論是否有一些計劃。 – chi