2013-03-19 57 views
2

從我的previous question,我一直在試圖找出一些單子代碼。首先,這裏是我使用一個狀態機功能:Haskell「無法推論」,「非類型變量參數」

import Control.Monad 
import Control.Monad.Error 

newtype FSM m = FSM { unFSM :: String -> m (String, FSM m) } 

fsm f []  = return [] 
fsm f (r:rs) = do 
    (xs, f') <- unFSM f r 
    liftM (xs:) (fsm f' rs) 

現在,這個編譯罰款:

exclaim :: (Monad m) => FSM m 
exclaim = FSM exclaim' 
exclaim' xs = return (xs ++ "!", exclaim) 

但是,這確實是因爲該類型聲明不是:

question :: (MonadError String m) => FSM m 
question = FSM question' 
question' xs 
    | last xs == '?' = throwError "Already a question" 
    | otherwise  = return (xs ++ "?", question) 

錯誤是Non type-variable argument,我認爲這是MonadError後面的String。如果我刪除了類型聲明,則代替Could not deduce。我明白啓用FlexibleContexts只是「修復」這個,但是有沒有更簡單的方法可以讓我拋出錯誤?我寧願不啓用各種編譯器擴展。

Full code here

+5

'FlexibleContexts'是一個非常無害的擴展。沒必要害怕這一點。如果沒有類型簽名,它還會編譯您是否禁用單態限制。 – 2013-03-19 17:06:14

+1

如果不需要擴展,只是對擴展有點不利。我寧願尋找替代方法,例如重構代碼。解決方案1是FlexibleContexts。還有其他建議嗎? – me2 2013-03-19 17:51:09

+0

或多或少,你要得到的所有解決方案都將是「打開FlexibleContexts」。不要害怕擴展,他們會幫助你。而這一個並不神祕。這是專門讓你做你想做的。沒有更多,沒有更多。 – Carl 2013-03-19 18:05:24

回答

4

如果你絕對不希望使用FlexibleContextsNoMonomorphismRestriction,可以使questionquestion'更普遍的一丁點,使其無需編譯你的模塊在開啓擴展:

question :: (Error e, MonadError e m) => FSM m 
question = FSM question' 

question' :: (Error e, MonadError e m) => String -> m (String, FSM m) 
question' xs 
    | last xs == '?' = throwError $ strMsg "Already a question" 
    | otherwise  = return (xs ++ "?", question) 

讓它扔一個Error類型,使用strMsg,並指定類型簽名。

但我仍然會選擇啓用FlexibleContexts

4

爲了闡述Daniel的答案,他的解決方案實際上是避免FlexibleContexts的一般解決方案。

你有這樣一個約束的任何時間:

(SomeTypeConstructor SomeType) => ... 

...其中SomeType是觸發FlexibleInstances警告一些具體的類型,你總是可以通過類型分級解決FlexibleContexts的操作要使用上SomeType,如:

class IsSomeType t where 
    get :: t -> SomeType 
    set :: SomeType -> t -> t 

...然後結合IsSomeType到你的約束:

(IsSomeType t, SomeTypeConstructor t) => ... 

...僅使用IsSomeType中的方法。

相關問題