簡體   English   中英

F#和Haskell中向量維數的類型約束(從屬類型)

[英]Type constraints on dimensionality of vectors in F# and Haskell (Dependent Types)

我是F#和Haskell的新手,並且正在實施一個項目,以確定我希望花更多時間來使用哪種語言。

在很多情況下,我希望給定的數值類型基於給頂層函數提供的參數(即在運行時)具有給定的尺寸。 例如,在此F#代碼段中,我有

type DataStreamItem = LinearAlgebra.Vector<float32>

type Ball =
    {R : float32;
     X : DataStreamItem}

我希望所有DataStreamItem類型的實例都具有D維。

我的問題是出於算法開發和調試的利益,因為這種形狀不匹配的錯誤可能很難確定,但是在算法運行時應該不是問題:

F#Haskell中 ,是否有辦法約束DataStreamItem和/或Ball具有D維? 還是在每次計算中都需要使用模式匹配?

如果是后者,是否有任何良好,輕量級的范式可以在發生此類約束違規時立即予以捕捉(在性能至關重要的情況下可以將其刪除)?

編輯:

為了闡明D被約束的意義:

D的定義是,如果將main(DataStream)函數的算法表示為計算圖,則所有中間計算都將依賴於D的維來執行main(DataStream) 我能想到的最簡單的例子是一個點的產品MDataStreamItem :的尺寸DataStream會確定的尺寸參數的創建M

另一個編輯:

一周后,我發現以下博客恰好​​概述了我在Haskell的依賴類型中尋找的內容:

https://blog.jle.im/entry/practical-dependent-types-in-haskell-1.html

另一個:Reddit包含有關Haskell中依賴類型的一些討論,並包含R. Eisenberg相當有趣的論文建議的鏈接。

Haskell不是F#類型的系統都不足以(直接)表達“ N個遞歸類型T的嵌套實例,其中N在2到6之間 ”或“ 一串正好為6個長的字符語句。 至少沒有確切的術語。

我的意思是,可以肯定,您始終可以表示這樣的6位長字符串類型,如type String6 = String6 of char*char*char*char*char*char或該類型的某種變體(從技術上講,這對於您的特定示例應該足夠了)向量,除非您沒有告訴我們整個示例),但是您不能說類似type String6 = s:string{s.Length=6} ,更重要的是,您不能定義concat: String<n> -> String<m> -> String<n+m>形式的函數concat: String<n> -> String<m> -> String<n+m> ,其中nm表示字符串長度。

但是你不是第一個問這個問題的人 這個研究方向確實存在,並且被稱為“ 從屬類型 ”,我可以將其要旨最概括地表達為“ 對類型進行更高階,更強大的運算 ”(與像聯合和交集相反,正如我們在ML語言)-請注意,在上面的示例中,我如何用數字而不是其他類型對String類型進行參數化,然后對該數字進行算術運算。

在這一方向上最傑出的語言原型(據我所知)是AgdaIdrisF *Coq (實際上不是完整的AFAIK)。 簽出它們,但要提防:這是明天的邊緣,我不建議基於這些語言啟動一個大型項目。

(編輯:顯然,您可以在Haskell中做一些技巧來模擬依賴類型,但這不是很方便,您必須啟用UndecidableInstances

或者 ,您可以選擇在運行時進行檢查的較弱解決方案。 一般要點是:將矢量類型包裝在普通包裝中,不允許直接構造,而是提供構造函數,並使這些構造函數確保所需的屬性(即,長度)。 就像是:

type Stream4 = private Stream4 of DataStreamItem
   with
      static member create (item: DataStreamItem) =
         if item.Length = 4 then Some (Stream4 item)
         else None

         // Alternatively:
         if item.Length <> 4 then failwith "Expected a 4-long vector."
         item

這是Scott Wlaschin提出的方法的更完整說明: 約束字符串

因此,如果我正確理解,您實際上並沒有執行任何類型級別的算術運算,而只是擁有一個“長度標記”,該“長度標記”在函數調用鏈中共享。

長期以來,在Haskell可以做到這一點。 我認為非常優雅的一種方法是使用所需長度的標准固定長度類型為數組添加注釋:

newtype FixVect v s = FixVect { getFixVect :: VU.Vector s }

為了確保正確的長度,您僅提供從固定長度類型構造的(多態) 智能構造函數,這是絕對安全的,盡管未提及實際的維數!

class VectorSpace v => FiniteDimensional v where
  asFixVect :: v -> FixVect v (Scalar v)

instance FiniteDimensional Float where
  asFixVect s = FixVect $ VU.singleton s
instance (FiniteDimensional a, FiniteDimensional b, Scalar a ~ Scalar b)        => FiniteDimensional (a,b) where
  asFixVect (a,b) = case (asFixVect a, asFixVect b) of
        (FixVect av, FixVect bv) -> FixVect $ av<>bv

由未裝箱的元組進行的構造實際上效率很低,但這並不意味着您可以使用這種范例編寫高效的程序–如果維度始終保持不變,則只需要包裝一次並解開包裝,就可以通過安全而又重要的操作來完成所有關鍵操作運行時未經檢查的拉鏈,折疊和LA組合。

無論如何,這種方法並未真正廣泛使用。 也許單個常量維度實際上對於大多數相關操作來說太過局限,並且如果您經常需要拆開元組,那么它效率太低。 目前流行的另一種方法是用類型級數字實際標記向量。 隨着GHC-7.4中數據類型的引入,這些數字已經以可用形式可用。 到目前為止,它們仍然很笨拙,不適合進行適當的算術運算,但是即將發布的8.0將大大改進Has​​kell中這種依賴類型編程的許多方面。

提供有效的長度索引數組的庫是linear的

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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