Created
July 23, 2015 20:21
-
-
Save roboguy13/7dff868dc066932ff65c to your computer and use it in GitHub Desktop.
Deep embedding simulating simple Arduino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE GADTs #-} | |
{-# LANGUAGE StandaloneDeriving #-} | |
{-# LANGUAGE FlexibleInstances #-} | |
module Client where | |
import Prelude hiding (read) | |
import qualified Prelude | |
import Data.List | |
import Control.Monad | |
import Server | |
import Debug.Trace | |
read :: Read a => String -> a | |
read s = case reads s of | |
[(x, "")] -> x | |
_ -> error $ "read: Cannot parse " ++ show s | |
data Action a where | |
Button :: Int -> Action (E Bool) | |
Led :: Int -> E Bool -> Action () | |
Wait :: Int -> Action () | |
data R a where | |
Action :: Action a -> R a | |
Bind :: R a -> (a -> R b) -> R b | |
Return :: a -> R a | |
Loop :: R () -> R () | |
instance Functor R where | |
fmap = liftM | |
instance Applicative R where | |
pure = return | |
(<*>) = ap | |
instance Monad R where | |
return = Return | |
(>>=) = Bind | |
buttonE :: Int -> R (E Bool) | |
buttonE = Action . Button | |
ledE :: Int -> E Bool -> R () | |
ledE i b = Action $ Led i b | |
loop :: R () -> R () | |
loop = Loop | |
-- waitE :: E Int -> R () | |
-- waitE = Action . Wait | |
wait :: Int -> R () | |
wait = Action . Wait | |
data E a where | |
Lit :: Bool -> E Bool | |
Not :: E Bool -> E Bool | |
lit :: Bool -> E Bool | |
lit = Lit | |
notE :: E Bool -> E Bool | |
notE = Not | |
deriving instance Show a => Show (E a) | |
deriving instance Show (Action a) | |
readEBool :: String -> Maybe (E Bool) | |
readEBool s = case stripPrefix "Not " s of | |
Just s' -> readEBool s' | |
_ -> case stripPrefix "Lit " s of | |
Just s' -> Just . Lit $ read s' | |
Nothing -> Nothing | |
readActionReply :: Action a -> String -> a | |
readActionReply (Button {}) s = case readEBool s of | |
Just b -> b | |
_ -> error $ "Invalid reply for Button: " ++ show s | |
readActionReply (Led {}) "()" = () | |
readActionReply (Led {}) s = error $ "Invalid reply for Led: " ++ show s | |
readActionReply (Wait {}) "()" = () | |
readActionReply (Wait {}) s = error $ "Invalid reply for Wait: " ++ show s | |
sendAction :: Action a -> Remote a | |
sendAction a = fmap (readActionReply a) $ runCommand $ show a | |
send :: R a -> Remote a | |
send (Return x) = return x | |
send (Bind m f) = send m >>= send . f | |
send (Action a) = sendAction a | |
send (Loop m ) = forever (send m) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Server | |
import Client | |
main :: IO () | |
main = runServer . send $ do | |
loop $ do | |
bE <- buttonE 0 | |
ledE 0 bE | |
ledE 1 (notE bE) | |
wait 100 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE OverloadedStrings #-} | |
{-# LANGUAGE StandaloneDeriving #-} | |
{-# LANGUAGE GADTs #-} | |
module Server (Remote, runServer, runCommand) where | |
import Prelude hiding (read) | |
import qualified Prelude | |
import Graphics.Blank hiding (send, wait) | |
import qualified Graphics.Blank as Blank | |
import Web.KeyCode | |
import Control.Monad.Reader | |
import Control.Monad.State.Strict | |
import Control.Concurrent | |
import Control.Lens | |
import Data.Maybe (fromMaybe) | |
import Data.List | |
import Debug.Trace | |
read :: Read a => String -> a | |
read s = case reads s of | |
[(x, "")] -> x | |
_ -> error $ "read: Cannot parse " ++ show s | |
type Remote = StateT ([Bool], [Bool]) (ReaderT DeviceContext IO) | |
data R | |
= Button Int | |
| Led Int (E Bool) | |
| Loop R | |
| Wait Int | |
deriving (Read) | |
data E a where | |
Lit :: Bool -> E Bool | |
Not :: E Bool -> E Bool | |
deriving instance a ~ Bool => Read (E a) | |
deriving instance Show a => Show (E a) | |
runCommand :: String -> Remote String | |
runCommand = runRCommand . read | |
evalE :: E a -> a | |
evalE (Lit b) = b | |
evalE (Not e) = not (evalE e) | |
runRCommand :: R -> Remote String | |
runRCommand (Button i) = fmap (traceId . show . Lit) $ button i | |
runRCommand (Led i b ) = fmap show $ led i (evalE b) | |
runRCommand (Loop r ) = fmap show $ loop r | |
runRCommand (Wait ms ) = fmap show $ wait ms | |
buttons :: Lens' ([Bool], [Bool]) [Bool] | |
buttons = _1 | |
leds :: Lens' ([Bool], [Bool]) [Bool] | |
leds = _2 | |
runServer :: Remote () -> IO () | |
runServer r = blankCanvas (3000 { events = ["keyup", "keydown"] }) | |
$ \context -> do | |
Blank.send context initUI | |
runReaderT (evalStateT r ([False, False, False, False], [False, False, False, False])) context | |
initUI :: Canvas () | |
initUI = do | |
drawLEDs [False, False, False, False] | |
-- drawButtons | |
stroke () | |
drawLEDs :: [Bool] -> Canvas () | |
drawLEDs [a, b, c, d] = do | |
beginPath () | |
arc (100+offset, 75, 20, 0, 2*pi, False) | |
closePath () | |
when a $ fill () | |
stroke() | |
beginPath () | |
moveTo (220+offset, 75) | |
arc (200+offset, 75, 20, 0, 2*pi, False) | |
closePath () | |
when b $ fill () | |
stroke() | |
beginPath () | |
moveTo (320+offset, 75) | |
arc (300+offset, 75, 20, 0, 2*pi, False) | |
closePath () | |
when c $ fill () | |
stroke() | |
beginPath() | |
moveTo (420+offset, 75) | |
arc (400+offset, 75, 20, 0, 2*pi, False) | |
closePath() | |
when d $ fill () | |
stroke() | |
where | |
offset = 35 | |
-- drawButtons :: Canvas () | |
-- drawButtons = do | |
-- strokeRect (60,300,100,50) | |
-- strokeRect (60 + 120,300,100,50) | |
-- strokeRect (60*2 + 180,300,100,50) | |
-- strokeRect (60*3 + 240,300,100,50) | |
button :: Int -> Remote Bool | |
button buttonNum = do | |
updateButtons | |
bs <- use buttons | |
return $ fromMaybe False $ bs ^? ix buttonNum | |
readButtonNum :: Key -> Maybe Int | |
readButtonNum KeyH = Just 0 | |
readButtonNum KeyJ = Just 1 | |
readButtonNum KeyK = Just 2 | |
readButtonNum KeyL = Just 3 | |
readButtonNum _ = Nothing | |
updateButtons :: Remote () | |
updateButtons = do | |
event <- lift $ lift . Blank.wait =<< ask | |
case readButtonNum =<< keyCodeLookup <$> eWhich event of | |
Just buttonNum -> | |
buttons . ix buttonNum .= (eType event == "keydown") | |
_ -> return () | |
led :: Int -> Bool -> Remote () | |
led ledNum state = do | |
leds . ix ledNum .= state | |
ledStates <- use leds | |
runBlank $ clearCanvas >> drawLEDs ledStates | |
where | |
runBlank :: Canvas a -> Remote a | |
runBlank c = do | |
context <- ask | |
liftIO $ Blank.send context c | |
loop :: R -> Remote () | |
loop r = forever $ do | |
runRCommand r | |
return () | |
wait :: Int -> Remote () | |
wait ms = liftIO . threadDelay $ ms*10 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment