簡體   English   中英

相當於Haskell中的C#通用接口

[英]Equivalent to C# generic interface in Haskell

我正在嘗試將一些簡單的C#代碼轉換為Haskell。 所以說我有一個簡單的不可變“數據庫”類型,它只是一個包含各種列表字段的記錄。 所以,說吧

data Person = Person { }
data Book = Book { }
data Database = Database { employees :: [Person], books :: [Book], customers :: [Person] }

現在我想創建一個類型類,表示“視圖”,或者本質上是該數據庫的“表”。

class Table r t where -- r is the record type (e.g. Person or Book)
  getRecords :: t -> Database -> [r]
  setRecords :: t -> [r] -> Database -> Database

然后我可以創建代表每個表的實例:

data ET = EmployeeTable
instance (Table Person) ET where
  getRecords t db = employees db
  setRecords t records db = Database records (books db) (customers db)

這就是我所擁有的,並且它可以工作,但{-# LANGUAGE MultiParamTypeClasses #-}是包含{-# LANGUAGE MultiParamTypeClasses #-} 否則,表類型類的定義失敗。

本身並不是什么大不了的事:它可以編譯和工作,但是快速閱讀MultiParamTypeClasses可以解決潛在的復雜問題(我還沒有花時間完全搞定它們)。

對我來說奇怪的是,這在C#中非常簡單。 假設記錄/數據庫類的簡單不可變定義,定義接口很簡單,然后實現遵循沒有問題。

interface ITable<TRecord> {
    TRecord[] GetRecords(Database db);
    Database SetRecords(TRecord[] records, Database db);
}

真的,這就是這個問題的本質。 是否有更慣用的方法將上述ITable<TRecord>接口所賦予的功能從C#轉換為Haskell? 我的理解是C#接口最接近Haskell類型類,所以這就是我想要做的。 但我覺得很奇怪,像通用接口這樣簡單的東西需要在Haskell中高度吹捧的類型系統中進行語言擴展。

(NB 為什么我要這樣做?為簡潔起見,上面的內容有點簡化,但一般來說,如果我創建了一個只有getIdRecord類型類的充實PersonBook實例,那么我可以支持CRUD函數對於整個數據庫非常一般:我只需要為Table類型類定義一次這些函數,它們將自動應用於DB中的所有表。這里是完整代碼底部的示例用法,以及它的C#等價物.https: deleteRecord 。注意Haskell中的deleteRecord不能編譯,因為r的類型無法推斷,而在C#中它編譯得很好。這增加了我的想法,也許MultiParamTypeClasses不是正確的做法。但如果不是,那又什么?)

更新

好吧,它聽起來像MultiParamTypeClasses這樣的評論很好。 所以現在我剩下的問題是如何修復鏈接的要點,以便deleteRecord將編譯?

deleteRecord :: (Table r t) => t -> Int -> Database -> Database

這是個問題。 這里有一個r的前=>但沒有r=> 給定一個函數調用像deleteRecord bookTable 1 db ,哈斯克爾不知道哪些r你在說什么。 雖然r應該完全由t決定,但Haskell無法知道這一點。 實際上,沒有人不允許這些實例定義:

instance Table Foo Bar
instance Table Foo Baz
instance Table Qux Bar
instance Table Qux Baz

“表”類型中沒有任何內容可以阻止這種情況。 它們只是沒有任何實際數據的空標簽。

事實上,您的模塊中只有一個感興趣的實例是無關緊要的,Haskell無法保證其他模塊的任何內容。

那你有什么選擇呢?

  1. 完全擺脫表格類型。 記錄類型就足夠了。
  2. 使用另一個Haskell擴展, FunctionalDependencies
  3. 使用另一個Haskell擴展, TypeFamilies

最后兩個擴展允許創建通用容器,這是數據庫表本質上的。

以下是Table類型在第一個擴展名中的外觀:

class Record r => Table r t | t -> r where ...

符號t -> r表示r完全由t確定(IOW r 功能上取決於 t )。 一旦Haskell看到一個實例Table Foo Bar ,就會知道沒有實例Table Qux Bar (編譯器會在看到相互沖突的定義時發出錯誤信號)。 這樣deleteRecord就是格式良好的。 r不在簽名中但是沒關系: t是已知的並且rt的函數。

我讓你自己弄清楚TypeFamilies 如今這是一種更受歡迎的解決方案。 FunctionalDependencies是一個較舊的擴展,它易於理解,但在某些極端情況下會導致復雜化。 不要擔心它們,在你看到之前你將成為Haskell的主人。

暫無
暫無

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

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