[英]In Haskell, why isn't there a TypeClass for things that can act like lists?
我正在閱讀Learn You a Haskell ,我想知道為什么這么多東西都像一個列表,而 Prelude 中沒有任何內容使用類型類的本機工具來設置它:
“ : 的字節串版本被稱為 cons 它接受一個字節和一個字節串並將該字節放在開頭。雖然它很懶惰,因此即使字節串中的第一個塊未滿,它也會創建一個新塊。這就是為什么如果要在字節串的開頭插入大量字節,最好使用 cons, cons' 的嚴格版本。”
為什么沒有可列出的TypeClass或提供:
函數來統一Data.ByteString
、 Data.List
、 Data.ByteString.Lazy
等的東西? 這是否有原因,或者這只是遺留 Haskell 的一個元素? 使用:
作為例子有點輕描淡寫,同樣來自 LYAH:
否則,字節串模塊有大量類似於 Data.List 中的函數,包括但不限於 head、tail、init、null、length、map、reverse、foldl、foldr、concat、takeWhile、filter , 等等。
ListLike包似乎提供了您正在尋找的東西。 我一直不明白為什么它不更受歡迎。
除了 List 之外,在 Prelude 中沒有實現這一點的一個原因是,如果不調用某些語言擴展(多參數類型類和基金或關聯類型),就不可能做得很好。 需要考慮三種容器:
這是一個非常基本的 ListLike 風格的類,沒有使用任何擴展:
class Listable container where
head :: container a -> a
instance Listable [] where
head (x:xs) = x
instance Listable ByteString where --compiler error, wrong kind
instance Listable SV.Vector where
head v = SV.head --compiler error, can't deduce context (Storable a)
這里container
有種類*->*
。 這不適用於字節串,因為它們不允許任意類型; 他們有種*
。 它也不適用於 Data.Vector.Storable 向量,因為該類不包含上下文(Storable 約束)。
您可以通過將類定義更改為
class ListableMPTC container elem | container -> elem where
或者
class ListableAT container where
type Elem container :: *
現在container
有 kind *
; 它是一個完全應用的類型構造函數。 也就是說,您的實例看起來像
instance ListableMPTC [a] a where
但你不再是 Haskell98。
這就是為什么即使是一個簡單的 Listable 類型的接口也是不平凡的。 當您要考慮不同的集合語義(例如隊列)時,它會變得有點困難。 另一個真正的大挑戰是可變數據與不可變數據。 到目前為止,我所看到的每一次嘗試(除了一次)都通過創建一個可變接口和一個不可變接口來解決這個問題。 我所知道的將兩者統一起來的一個界面令人費解,調用了一堆擴展,並且性能很差。
附錄:字節串
完全是我的猜想,但我認為我們堅持將字節串作為進化的產物。 也就是說,它們是低性能 I/O 操作的第一個解決方案,使用Ptr Word8
s 與 IO 系統調用接口是有意義的。 對指針的操作需要 Storable,而且很可能當時還沒有使多態起作用的必要擴展(如上所述)。 現在很難克服他們的勢頭。 具有多態性的類似容器當然是可能的,storablevector 包實現了這一點,但它並沒有那么流行。
字節串可以是多態的,對元素沒有任何限制嗎? 我認為最接近 Haskell 的是 Array 類型。 這幾乎不如低級 IO 的字節串好,因為數據需要從指針解包到數組的內部格式。 此外,數據是裝箱的,這增加了顯着的空間開銷。 如果你想要未裝箱的存儲(更少的空間)和與 C 的高效接口,指針是要走的路。 一旦你有了 Ptr,你就需要 Storable,然后你需要在類型類中包含元素類型,這樣你就需要擴展了。
話雖如此,我認為有了適當的擴展可用,對於任何單個容器實現(模可變/不可變 API)來說,這基本上是一個已解決的問題。 現在更難的部分是提出一組合理的類,這些類可用於許多不同類型的結構(列表、數組、隊列等),並且足夠靈活以供使用。 我個人認為這相對簡單,但我可能是錯的。
這樣一個類的主要問題是,即使它存在,它也只能提供表面的相似性。
使用不同結構構建的相同算法的漸近性會有很大差異。
在嚴格字節串的情況下,用 cons 構建它們是可怕的,因為每次添加另一個 Char 時,你最終都會復制整個字符串。 列表上的此O(1)操作將其轉換為 Bytestring 上的O(n)操作。
當您實現可能想到的第一個算法 map 時,這會導致O(n^2)行為,而使用 cons 構建列表或 Data.Sequence.Seq 是線性時間,它可以在O(n) 中實現對於字節串或向量以及稍加思考。
事實證明,鑒於這一點,這樣一個類的效用比實際更膚淺。
我並不是說找不到好的設計,但這樣的設計很難使用和優化,而且設計的可用版本很可能不會成為 Haskell 98。
我已經在我的 keys 包中擠出了這個設計空間的一部分,它提供了很多用於索引容器等的功能,但我故意避免提供類似列表的 API a.) 因為它之前已經完成了由於上述漸近問題,幾乎沒有成功和 b.)。
tl;dr當基礎操作的漸近線發生變化時,您通常希望以非常不同的方式實現算法。
提供:功能來統一 Data.ByteString、Data.List、Data.ByteString.Lazy 等?
已經嘗試提出一個好的 a) 序列接口和 b) 容器接口,但是,統一不同類型的數據類型,具有不同的類型約束,通常導致結果不夠標准,難以想象將它們放在基礎庫中。 與數組類似,盡管 Vector 包現在有一個相當通用的接口(基於關聯的數據類型)。
有幾個項目將這些不同的半相關數據類型統一到一個單一的接口中,所以我希望我們很快就能看到結果。 容器類型也是如此。 結果不會是微不足道的。
有兩種類型的類,稱為Foldable
和Traversable
,旨在抽象列表和其他順序數據結構的一些常見行為。 並非所有數據結構都有這些實例,我不知道它們對編譯器是否足夠透明,以便它仍然可以對它們執行優化(有人知道嗎?)
資料來源:可折疊和可遍歷
另請參閱為什么 Haskell 缺少“明顯的”類型類的答案
實際上在GHC.Exts
有一個OverloadedLists
擴展和一個IsList
類來配合它。
ByteString 不是泛型類型。
在其他語言中,所有類似列表的數據結構都有類似Sequence
東西。 我認為這有效,具有正確的擴展名:
class Seq a b | a -> b where
head :: a -> b
isTail :: a -> Bool
# ([a]) is a sequence of a's
instance Seq [a] a where
head (x:xs) = x
isTail = (== [])
# ByteString is a sequence of chars
instance Seq ByteString Char
或者試試這個?
type BS a = ByteString
instance List BS
在 Haskell 中為類似列表的數據創建一個類型類並沒有多大價值。 為什么? 因為懶惰。 您只需編寫一個將數據轉換為列表的函數,然后使用該列表即可。 該列表只會在需要其子列表和元素時構建,並且一旦沒有對前綴的引用,它們的內存將有資格進行收集。
提供泛型toList
函數的類型類是toList
——但是,它已經存在於Data.Foldable
。
所以基本上,解決方案是實現Data.Foldable
並使用其toList
函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.