{-# LANGUAGE LambdaCase #-}
作爲類型類實例的函數?
我有一堆以各種方式編碼失敗的函數。例如:
f :: A -> Bool
回報False
失敗g :: B -> Maybe B'
回報Nothing
失敗h :: C -> Either Error C'
回報Left ...
失敗
我想鏈以同樣的方式,這些操作爲Maybe
單子,所以鏈接函數需要知道每個函數是否失敗,然後才能繼續下一個函數。爲此,我寫了這個類:
class Fail a where
isFail :: a -> Bool
instance Fail() where
isFail() = False
instance Fail Bool where -- a
isFail = not
instance Fail (Maybe a) where -- b
isFail = not . isJust
instance Fail (Either a b) where -- c
isFail (Left _) = True
isFail _ = False
但是,它可能是不符合功能存在:
f' :: A -> Bool
回報True
失敗g' :: B -> Maybe Error
回報Just Error
失敗(Nothing
成功)h' :: C -> Either C' Error
返回Right ...
失敗
這些可以通過簡單地與改造他們,例如功能包它們來補救:
f'' = not . f'
。g'' = (\case Nothing -> Right(); Just e -> Left e) . g'
h'' = (\case Left c -> Right c; Right e -> Left e) . h'
然而,鏈接功能的用戶希望能夠結合f
,g
,h
,f'
,g'
和h'
並讓他們只是工作。他不會知道函數的返回類型需要被轉換,除非他查看了他所組合的每個函數的語義,並檢查它們是否與範圍內的任何其他實例匹配。對於普通用戶來說,這是單調乏味而且太微妙的,尤其是對於繞過用戶不得不選擇正確實例的類型推斷而言。
這些函數不是在知道如何使用它們的情況下創建的。所以我可以創建一個類型data Result a b = Fail a | Success b
並在每個函數週圍創建包裝。例如:
fR = (\case True -> Sucess(); False -> Fail()) . f
f'R = (\case False -> Sucess(); True -> Fail()) . f'
gR = (\case Just a -> Sucess a; Nothing -> Fail()) . g
g'R = (\case Nothing -> Sucess(); Just e -> Fail e) . g'
hR = (\case Left e -> Fail e; Right a -> Sucess a) . h
h'R = (\case Right e -> Fail e; Left a -> Sucess a) . h'
然而,這種感覺很髒。我們正在做的僅僅是證明/解釋如何在組合函數的上下文中使用f
,g
,h
,f'
,g'
和h'
中的每一個。是否有更直接的方式來做到這一點?我要的到底是一個方式說,應使用其Fail
類型類的實例爲每個功能,即,(使用提供給上述類型類實例的名稱),f
→a
,g
→b
,h
→c
,並f'
→a'
,g'
→b'
,h'
→c'
爲「無效」的功能,其中a'
,b'
和c'
被定義爲以下情況(重疊於以前的,所以你需要能夠通過名稱來接他們不知何故):
instance Fail Bool where -- a'
isFail = id
instance Fail (Maybe a) where -- b'
isFail = isJust
instance Fail (Either a b) where -- c'
isFail (Right _) = True
isFail _ = False
雖然不一定要通過類特性來完成。也許有一些方法可以做到這一點,而不是類型類?
如果你想鏈接他們像一個monad(與'do'符號),那麼你會需要將它們全部轉換爲單個類型,然後您可以創建一個Monad實例。您聲明您希望類型系統只是在沒有提示或上下文的情況下通過失敗找出函數意味着什麼。理論上,我可以有無數個函數,每個函數都返回一個不同的Integer表示失敗,編譯器應該如何知道特定的Integer何時失敗?它僅作爲上下文具有價值。你不能指望編譯器爲你編寫程序,否則我們都會使用Agda。 – bheklilr 2015-02-23 22:22:12
是的,我不一定要把它變成一個monad,但它會是類似的。我不希望編譯器知道哪些整數是失敗的,我想以某種方式指出哪些整數是'f'的失敗,併爲'f2'指示一個不同的集合,依此類推。例如,在Python中,我可以將一個全局變量映射函數作爲類型實例的一些等價物。那麼如果沒有用戶想要使用的某個函數的映射,它會在運行時崩潰而不是編譯時。但它會更安全。 – 2015-02-23 22:26:00