Globidev / twitch-irc

A base for working with Twitch IRC chatrooms
BSD 3-Clause "New" or "Revised" License
2 stars 2 forks source link

[IPC] Scripts can now send Actions to the master #5

Closed Globidev closed 9 years ago

Globidev commented 9 years ago

They use a piped stderr (This might be better to use fifos in the future)

rvion commented 9 years ago

I'm not sure about fifo if you want windows compatibility. As of now, you are windows compatible, and it is easy to test. Unless there is a clear ferformance need for fifo, I have the feeling that stdin and stdout are good ideas.

rvion commented 9 years ago

:warning:

I write below how you could give irc Scripters the ability to manipulate life of players in the game you're broascasting via scripts, even if they have absolutely no way to get that data themselves. It will even works for scripts connected remotely to your irc App

one will be able to write

then you can use in your Script:

...  -> print $ do 
           globiLife <- playerLife "globi"
           rvionLIfe <- playerLife "rvion"
           sendMessage $ (if globiLife > rvionLife then "globi" else "rvion") ++ " has the lead"
           replicateM 10 $ sendMessage "hello"
           return ()

and the whole expression will just be an AST that the App will evaluate later, with access to playerLife

Let's speak of Free monads

free monads allows you to decoupe the code and it's evaluation. It allows Script to build an AST with nice syntax and return it to App for App to evaluate it. It also allows you to switch which evaluator to run at runtime.

free-monads allow you to build expressions, and interpret them later. In your case, you could turn

data Action
  = SendMessage Channel String
  | SendPong String deriving (Show, Read, Eq)

into ( I also add a new action to better show what becomes possible)

data Action next
  = SendMessage Channel String next
  | SendPong String next
  | PlayerLife String (Int -> next)
  | Done
  deriving (Functor, Show, Read, Eq) -- use DeriveFunctor extension if you don't want to write the functor instance by hand.

the instance looks like that anyway, if you prefer to write it by hand:

instance Functor Action where
    fmap f (SendMessage c s next) = SendMessage c s (f next)
    fmap f (SendPong s next ) = SendPong s (f next)
    fmap f (PlayerLife s nextF ) = PlayerLife s (f . nextF)
    fmap f Done = Done

then you

type Script = Free Interaction

then you make your nice dsl builders (not needed, but that will allow you to only export those nice functions to you final user, and he'll have an orgasmic experience):

sendPong :: String -> Script ()
sendPong s = liftF (SendPong s ())

playerLife :: String -> Script Int
playerLife s = liftF (PlayerLife s id)

sendMessage :: Channel -> String -> Script ()
sendMessage s = liftF (SendMessage c s ())

then you can use in your Script:

...  -> print $ do 
           globiLife <- playerLife "globi"
           rvionLIfe <- playerLife "rvion"
           sendMessage $ (if globiLife > rvionLife then "globi" else "rvion") ++ " has the lead"
           replicateM 10 $ sendMessage "hello"
           return ()

The amazing part is that all the do bloc here has type Script (). No IO is done, the whole AST is serialized :) and in can be interpreted in App with an interpreter like that

interpret_v1 :: Script a -> IO a
interpret_v1 prog = case prog of
    Free (PlayerLife playerName g) -> do
        life <- ..... -- some IO code checking the player Life
        interpret (g life)
    Free (SendMessage c m next) -> do
        Twitch.sendMessage client c m
        interpret next
    ....

see here for more infos: http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html

Globidev commented 9 years ago

Should be fine for now Will definitely dig into free monads later though