我希望能夠使用GHC OverloadedStrings
擴展來創建IsString
實例,以便我的實例拒絕一些文字是無效的,並且使得拒絕發生在編譯時,因此編程錯誤不會將其編碼到代碼I中給我的用戶。如何獲得IsString實例中文字的編譯時驗證?
我有幾個使用案例,我有一個Name
類型只承認某些字符串。例如
module Name (Name(getName), makeName) where
import Data.Text (Text)
import qualified Data.Text as Text
-- | A guaranteed non-empty name.
newtype Name = Name { getName :: Text } deriving (Eq, Show, Ord)
makeName :: Text -> Maybe Name
makeName name
| Text.null name = Nothing
| otherwise = Just name
在一個真實的使用案例中,我會檢查有效字符,而不是以數字開頭,這樣的事情。
這個想法是我們不導出Name
構造函數,這意味着任何使用Name
值的人都可以相信它具有某些屬性(在這種情況下非空)。
我的問題是,我想在很多地方使用文字名稱。例如
programName :: Name
programName = fromJust $ makeName "the-great-and-powerful-turtle"
因爲我做了很多,我定義的unsafeMakeName
幫手,做幾乎同樣的事情:
unsafeMakeName :: Text -> Name
unsafeMakeName name = fromMaybe (error $ "Invalid name: " <> Text.unpack name) (makeName name)
這種方法的問題是,即使錯誤的原因是一個編程錯誤,直到運行時我才發現它。
我想要做的是爲Name
寫一個IsString
實例,它會進行驗證,例如,
instance IsString Name where
fromString = unsafeMakeName . Text.pack
...但在編譯時得到文字中無效名稱的錯誤。
當我嘗試這個,我似乎只是在運行時,當使用文字值時,得到錯誤。這不太理想,因爲這是我實際代碼中的錯誤。
有什麼辦法可以做到這一點? GHC可以解決這個問題嗎?請注意,我已經在那裏filed a bug。
我認爲做到這一點的唯一方法是使用模板Haskell通過編譯時拼接進行驗證。 –
對於重量較重的物體,您可以嘗試使用液體哈斯克爾的總體檢查器。至少它會很有趣。 –
這對於'printf'及其格式字符串來說是很好的例子。 – chi