I'm not sure how do I even call the thing I want to do. Nor do I know how to do it :D
Okay, in the end I want to get interactive program with output like this:
Tick
<spacebar press>
Tock
<spacebar press>
Tick
...and so on ad infinitum till ^C do us part
Condensed minimal example from actual code. Written in browser, so may not compile, but gives general idea.
data ClockEffect m a where
Change :: ThingEffect m ()
makeSem ''ClockEffect
data Clock = Tick | Tock
reinterpretClockToStateWithTrace :: Member Trace r => Sem (ClockEffect ': r) a -> Sem r a
reinterpretClockToStateWithTrace = reinterpret $ \case
Change -> do current <- get
case current of
Tick -> trace "Tock" >> put Tock
Tock -> trace "Tick" >> put Tick
driveClockFromKeyboard :: Member (Embed IO) r => _ -> _ -- I have no idea if this should produce ClockEffect or handle it or what
driveClockFromKeyboard = do forever $ void hGetChar >> change
runClockIO :: IO ()
runClockIO = runM . traceToIO . reinterpretClockToStateWithTrace . driveClockFromKeyboard -- or something like that?
Oh yeah, I'm using Polysemy-1.0.0.0 from stackage.
Just as usual after posting a question somewhere I solved it somehow on my own. Why I'm not so clever until I actually post something?
Anyway, solution:
keyboardClock :: Members [Embed IO, ClockEffect] r => Sem r a
keyboardClock = do embed prepareIO
forever $ waitForInput >> change
test :: IO ()
test = void
. runM
. traceToIO
. runState Low
. reinterpretClockToState
$ keyboardClock
Apparently I was right with type of keyboardClock initially, but mistaken in test it should've had $ instead of . before last effect.
Asking the question often clarifies your thoughts enough to help you see the answer yourself. My old boss wanted to get me a teddy bear to talk to, so I wouldn't have to tell him my problems only to solve them myself without any input from him.
your example can be implemented like this:
you want is to execute change forever
you want "change" to mean "output Tick, input a char, output Tock, input a char"
where "output" means to putStrLn and "input" means to getChar
translated into a program:
{-# LANGUAGE TemplateHaskell , DataKinds , FlexibleContexts , GADTs , LambdaCase , PolyKinds , RankNTypes , ScopedTypeVariables , TypeApplications , TypeOperators , TypeFamilies , BlockArguments #-}importPolysemyimportPolysemy.Embed(embed)importPolysemy.Input(Input,input,runInputSem)importPolysemy.Output(Output,output,runOutputSem)importControl.Monad(forever)dataClockEffectmawhereChange::ClockEffectm()makeSem''ClockEffectclockTickInputTockInput::Members'[Input Char, Output String]r=>Sem(ClockEffect': r) a -> Sem r aclockTickInputTockInput=interpret\caseChange->dooutput"Tick"_<-input@Char-- these should be unnecessary with the pluginoutput"Tock"_<-input@Charpure()-- it's important to leave the list of effects polymorphic:-- * we can reuse program in more places now-- * clockTickInputTockInput won't be able to work if we specify the effect list entirely, because it requires the list to contain Input and Output-- the instance resolution mechanism handles this, instantiating to the appropriate list of effectsprogram::MemberClockEffectr=>Semr()program=foreverchange-- runOutputSem allows us to treat every output action as a monadic action with access to the output value-- runInputSem allows us to treat every input action as a monadic action, providin the input value-- embed allows us to "embed" monads in our effect stack - IO in this case-- and when we're left with only Embed IO in our list, we can dispatch it with runMmain::IO()main=runM$runOutputSem(embed.putStrLn)$runInputSem(embedgetChar)$clockTickInputTockInputprogram
@Alex Chapman yes, just a usual reinterpreter from effect to another. @Georgi Lyubenov // googleson78 not slow, but "gave a additional pov". I didn't even thought about using Input/Output effects. That should be more clear, actually - I also want Clock to be actual clock with tunable frequency and maybe pure thing for tests. Using Input/Output gives flexibility to achieve that. So thank you
I'm not sure how do I even call the thing I want to do. Nor do I know how to do it :D
Okay, in the end I want to get interactive program with output like this:
Condensed minimal example from actual code. Written in browser, so may not compile, but gives general idea.
Oh yeah, I'm using Polysemy-1.0.0.0 from stackage.
Just as usual after posting a question somewhere I solved it somehow on my own. Why I'm not so clever until I actually post something?
Anyway, solution:
Apparently I was right with type of keyboardClock initially, but mistaken in test it should've had
$
instead of.
before last effect.Asking the question often clarifies your thoughts enough to help you see the answer yourself. My old boss wanted to get me a teddy bear to talk to, so I wouldn't have to tell him my problems only to solve them myself without any input from him.
https://en.wikipedia.org/wiki/Rubber_duck_debugging
Articulating your problem activates additional neural pathways :smiley:
So I guess your reinterpretClockToState has a type like
Sem (ClockEffect ': r) a -> Sem (State Clock ': r) a
?your example can be implemented like this:
you want is to execute
change
foreveryou want "change" to mean "output Tick, input a char, output Tock, input a char"
where "output" means to
putStrLn
and "input" means togetChar
translated into a program:
guess I was too slow :D
@Alex Chapman yes, just a usual reinterpreter from effect to another.
@Georgi Lyubenov // googleson78 not slow, but "gave a additional pov". I didn't even thought about using Input/Output effects. That should be more clear, actually - I also want Clock to be actual clock with tunable frequency and maybe pure thing for tests. Using Input/Output gives flexibility to achieve that. So thank you