Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.
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:
...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 ()
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-188.8.131.52 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?
keyboardClock :: Members [Embed IO, ClockEffect] r => Sem r a
keyboardClock = do embed prepareIO
forever $ waitForInput >> change
test :: IO ()
test = void
. runState Low
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.
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?
Sem (ClockEffect ': r) a -> Sem (State Clock ': r) a
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:
import Polysemy.Embed (embed)
import Polysemy.Input (Input, input, runInputSem)
import Polysemy.Output (Output, output, runOutputSem)
import Control.Monad (forever)
data ClockEffect m a where
Change :: ClockEffect m ()
clockTickInputTockInput :: Members '[Input Char, Output String] r => Sem (ClockEffect ': r) a -> Sem r a
clockTickInputTockInput = interpret \case
Change -> do
_ <- input @Char -- these should be unnecessary with the plugin
_ <- input @Char
-- 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 effects
program :: Member ClockEffect r => Sem r ()
program = forever change
-- 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 runM
main :: IO ()
$ runOutputSem (embed . putStrLn)
$ runInputSem (embed getChar)
$ clockTickInputTockInput 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