2016-08-08 53 views
0

ich eine slackbot mit dieser Bibliothek zu bauen bin versucht: https://hackage.haskell.org/package/slack-api, nur ein wenig mehr Haskell, zu lernen und hoffentlich endlich verstehen Monaden -_-.Fehlende Monadstate Instanz

ich dann die folgenden Typen haben:

data BotState = BotState 
    { 
    _appState :: AppState 
    } 

makeLenses ''BotState 


type AppState = HM.Map String ChannelState 

emptyState :: AppState 
emptyState = HM.empty 

data ChannelState = ChannelState 
{ _counter :: Int} 

type Bot = Slack.Slack BotState 

und ich betreibe meine bot mit:

initApp = lookupEnv "SLACK_API_TOKEN" >>= 
    \apiToken -> case apiToken of 
    Nothing -> throwM ApiTokenMissingException 
    Just t -> void $ Slack.runBot (Slack.SlackConfig t) runApp $ BotState emptyState 

wo:

runApp :: Slack.Event -> Bot() 
runApp [email protected](Slack.Message cid uid body _ _ _) = sendMessage cid "GAH I CAN HAZ CHZBURGHER!" 

Dieses feine läuft, jetzt möchte ich hinzufügen die Fähigkeit, den Systemzustand zu aktualisieren (durch Inkrementieren des Zählers oder auf andere Weise).

so eine modifyState Funktion meiner Bot ich hinzufügen:

modifyState :: (AppState -> AppState) -> Bot() 
modifyState f = uses Slack.userState $ view appState >>= 
    \state -> modifying Slack.userState $ set appState $ f state 

Dies bricht mit:

No instance for (Control.Monad.State.Class.MonadState 
          (Slack.SlackState BotState) ((->) BotState)) 
      arising from a use of ‘modifying’ 
     In the expression: modifying Slack.userState 
     In the expression: 
      modifying Slack.userState $ set appState $ f state 
     In the second argument of ‘(>>=)’, namely 
      ‘\ state -> modifying Slack.userState $ set appState $ f state’ 

Welche Sinn die Signatur für modifying gegeben macht:

modifying :: MonadState s m => ASetter s s a b -> (a -> b) -> m() 

jedoch auf für Slack.userState in der Dokumentation suchen:

userState :: forall s s. Lens (SlackState s) (SlackState s) s s Source 

Und dann:

data SlackState s 

... Constructor ... 
    Instances 
Show s => Show (SlackState s)Source 
MonadState (SlackState s) (Slack s)Source 

Also warum dann nicht die BotState bereits eine Instanz von MonadState? Wie könnte ich das beheben?

+2

Weil es nicht sucht' MonadState (SlackState BotState) BotState ', es sucht' MonadState (SlackState BotState) ((->) BotState) '. Scheint wie ein Präzedenzfall aufgrund '$' und '>> =' - explizite Klammern zu versuchen. – user2407038

+0

Du hast Recht, es war wegen der '$' in 'verwendet Slack.userState $ view appState'. Ändern dass 'verwendet Slack.userState (Ansicht AppState)' das Problem behebt. Ich würde gerne verstehen, was dort passiert ist? Warum hat sich das '$' in dieser speziellen Situation nicht wie erwartet verhalten? Vielen Dank für Ihre Hilfe, wenn Sie die Punkte sammeln möchten, schreiben Sie es als Antwort auf und erhalten Sie eine positive Bewertung/Annahme :) –

+0

Sehr grob kann man sich '$' vorstellen, indem man bis zum Ende Klammern hinzufügt der Ausdruck. Also, 'verwendet Slack.userState $ stuff1 >> = stuff2' ist' verwendet Slack.userState (stuff1 >> = stuff2) 'was nicht beabsichtigt war. Beachten Sie, dass Sie in einem 'do'-Block dieses Problem nicht haben: versuchen Sie' do state <- verwendet Slack.userState $ view appState; modifizieren Slack.userState $ set appState $ f state'. Sie können eine neue Zeile anstelle von ';' verwenden, solange Sie sie richtig einrücken ('state <-' und' modifying 'sollten in derselben Spalte beginnen) – chi

Antwort

2

$ Operator hat fixity 0, während >>= fixity hat 1, so Code wie dies funktionieren würde:

main :: IO() 
main = do 
    putStrLn "hello world" >>= \_ -> putStrLn "hi" 

Aber nicht dieses:

main :: IO() 
main = do 
    putStrLn $ "hello world" >>= \_ -> putStrLn "hi" 

Es wird interpretiert als:

main :: IO() 
main = do 
    putStrLn ("hello world" >>= \_ -> putStrLn "hi") 

fixity Informationen zu sehen, verwenden ghci ‚s :info Befehl:

:info $ 
($) :: 
    forall (r :: ghc-prim-0.5.0.0:GHC.Types.RuntimeRep) a (b :: TYPE 
                   r). 
    (a -> b) -> a -> b 
    -- Defined in ‘GHC.Base’ 
infixr 0 $ 
:info >>= 
class Applicative m => Monad (m :: * -> *) where 
    (>>=) :: m a -> (a -> m b) -> m b 
    ... 
    -- Defined in ‘GHC.Base’ 
infixl 1 >>= 

Auch, wenn Sie sich nicht sicher sind, gute alte Klammern sind immer für die Rettung :)