[英]Haskell type representing a subset of another type
我認為當給定指定類型的參數時,函數永遠不會失敗。
但是看看這個看似無害的代碼:
readInts :: String -> [Int]
readInts = map read . words
它的類型簽名過於籠統,事實上,對於任何不是由空格分隔的整數組成的字符串,它都會失敗,其類型應為:
readInts :: SpaceSeparatedIntegersString -> [Int]
當您嘗試構建不遵守規定標准的SpaceSeparatedIntegersString
時程序失敗。
實現SpaceSeparatedIntegersString
是一個明智的想法嗎?
如果是我怎樣才能實現這樣的類型? (我只是問一個一般的想法/提示/推動正確的方向,而不是完整的代碼)
我是否應該接受當String
格式不正確時我的函數將失敗(也就是說,我在問題開頭的陳述是錯誤的)?
我應該在函數定義中使用保護子句嗎?
你的目標是高尚的,但需要非常精確的類型。 依賴類型,例如Agda,Coq,Idris和其他語言中提供的類型可以實現您的建議。 例如在Coq,
Definition SpaceSeparatedIntegersString: Type :=
{ s: string | spaceSeparatedIntegers s } .
Definition spaceSeparatedIntegers (s: String): Prop := ...
問題在於,無論誰想要構造SpaceSeparatedIntegersString
值,都必須提供相關屬性的正式證明。 這是可行的,但需要一些關心,時間和數學技能。
在Haskell中,將類型保證移動到輸出類型更為常見。 我們可以使輸出類型更弱,而不是使輸入類型更強:
readInts :: String -> Maybe [Int]
這不是那么精確,但可以在不必證明任何事情的情況下使用。
或者,通過在模塊中聲明它而不導出其值構造函數,使SpaceSeparatedIntegersString
成為不透明類型。
module Foo(SpaceSeparatedIntegersString(), ...)
data SpaceSeparatedIntegersString = S String -- S is private
這樣,模塊的用戶只能通過模塊導出的功能來操作它。 需要注意的是,可以制作一組(導出的)組合器,以保證SpaceSeparatedIntegersString
類型的值永遠不會包含無效字符串。 例如,
combine :: SpaceSeparatedIntegersString
-> SpaceSeparatedIntegersString
-> SpaceSeparatedIntegersString
combine (S x) (S y) = S (x ++ " " ++ y)
這並不完全是直截了當的 - 如果你想允許一般的字符串操作,你可能會在某些情況下最終返回Maybe ...
您可能不應該實現SpaceSeparatedIntegerString
。 如果你做了抽象數據類型將是最簡單的事情。 你不應該接受你的功能應該失敗,至少不是在“模式匹配失敗”的意義上。 你應該創建一個返回Maybe [Int]
類的函數。
你不應該實現SpaceSeparatedIntegerString
的原因是可能只是將問題推到別處。 您可能從某些輸入獲取String
,所以在某處您需要一個函數String -> SpaceSeparatedIntegerString
,這也可能會失敗。 在某些時候,您需要從結構較少的數據轉換為更結構化的數據,除非您的唯一值是完全靜態指定的。
從結構較少的數據到更結構化的數據(或者更少類型的數據到更多類型的數據)的關鍵點實際上總是部分操作。 實際上,解析是將較少結構化數據轉換為更結構化數據的過程。 你應該做的,不過,在與你的原則,就是盡可能的實際具體化回收的結構入式系統。 所以isValidUri :: String -> Bool
是壞的,因為makeUri :: String -> Maybe String
,但是makeUri :: String -> Maybe Uri
,其中Uri
是一個可以完全代表有效URI的類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.