Making the controllers more linear
Basically, the idea of `(oneBranch <> secondBranch)` instead of ``` do x <- foo case x of ... ```
This commit is contained in:
1 parent
617bd255ff
commit
15e50322c4
4 files changed
+51
-17
No files matched your search
+27
-3
@@ -1,4 +1,6 @@
|
|||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# LANGUAGE Arrows #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
|
||||||
module AFRP
|
module AFRP
|
||||||
( Mealy(..)
|
( Mealy(..)
|
||||||
@@ -18,8 +20,10 @@ module AFRP
|
|||||||
, changes
|
, changes
|
||||||
, whenA
|
, whenA
|
||||||
, filterA
|
, filterA
|
||||||
|
, filterEvent
|
||||||
, thenA
|
, thenA
|
||||||
, (>>|)
|
, (>>|)
|
||||||
|
, (>>||)
|
||||||
, toEvent
|
, toEvent
|
||||||
, lMerge
|
, lMerge
|
||||||
, Pair(..)
|
, Pair(..)
|
||||||
@@ -271,7 +275,7 @@ data Mealy eff a b = Mealy
|
|||||||
|
|
||||||
-- Provide a name
|
-- Provide a name
|
||||||
named :: Text -> Mealy eff a b -> Mealy eff a b
|
named :: Text -> Mealy eff a b -> Mealy eff a b
|
||||||
named n f = f{runDiagramC = \run -> run (Step n (emptyDiagram run))}
|
named n f = f{runDiagramC = \run -> run (Step n (runDiagramC f run))}
|
||||||
|
|
||||||
instance Semigroup b => Semigroup (Mealy eff a b) where
|
instance Semigroup b => Semigroup (Mealy eff a b) where
|
||||||
Mealy ast ad af <> Mealy bst bd bf = Mealy (ast <> bst) (combineDiagram Merge ad bd) $ \nt -> af nt <> bf nt
|
Mealy ast ad af <> Mealy bst bd bf = Mealy (ast <> bst) (combineDiagram Merge ad bd) $ \nt -> af nt <> bf nt
|
||||||
@@ -290,15 +294,19 @@ eff f = Mealy mempty emptyDiagram $ \nt -> do
|
|||||||
instance Category (Mealy eff) where
|
instance Category (Mealy eff) where
|
||||||
{- HLINT ignore "Use const" -}
|
{- HLINT ignore "Use const" -}
|
||||||
id = Mealy mempty emptyDiagram $ \_nt -> id
|
id = Mealy mempty emptyDiagram $ \_nt -> id
|
||||||
(Mealy ast ad f) . (Mealy bst bd g) = Mealy (ast <> bst) (combineDiagram Then ad bd) $ \nt -> do
|
(Mealy ast ad f) . (Mealy bst bd g) = Mealy (ast <> bst) (combineDiagram Then bd ad) $ \nt -> do
|
||||||
f nt . g nt
|
f nt . g nt
|
||||||
|
|
||||||
instance Arrow (Mealy eff) where
|
instance Arrow (Mealy eff) where
|
||||||
arr f = Mealy mempty emptyDiagram $ \_nt -> arr f
|
arr f = Mealy mempty emptyDiagram $ \_nt -> arr f
|
||||||
first (Mealy st d f) = Mealy st d $ \nt -> first (f nt)
|
first (Mealy st d f) = Mealy st d $ \nt -> first (f nt)
|
||||||
|
(Mealy ast ad f) &&& (Mealy bst bd g) = Mealy (ast <> bst) (combineDiagram Both ad bd) $ \nt -> do
|
||||||
|
f nt &&& g nt
|
||||||
|
|
||||||
instance ArrowChoice (Mealy eff) where
|
instance ArrowChoice (Mealy eff) where
|
||||||
left (Mealy st d f) = Mealy st d $ \nt -> left (f nt)
|
left (Mealy st d f) = Mealy st d $ \nt -> left (f nt)
|
||||||
|
(Mealy ast ad f) +++ (Mealy bst bd g) = Mealy (ast <> bst) (combineDiagram Branch ad bd) $ \nt -> do
|
||||||
|
f nt +++ g nt
|
||||||
|
|
||||||
|
|
||||||
instance Functor (Mealy eff a) where
|
instance Functor (Mealy eff a) where
|
||||||
@@ -486,13 +494,29 @@ whenA predicate auto = arr (\a -> if predicate a then Left a else Right ()) >>>
|
|||||||
filterA :: (a -> Bool) -> Mealy eff a (Either () a)
|
filterA :: (a -> Bool) -> Mealy eff a (Either () a)
|
||||||
filterA f = arr $ \a -> bool (Left ()) (Right a) (f a)
|
filterA f = arr $ \a -> bool (Left ()) (Right a) (f a)
|
||||||
|
|
||||||
|
-- filters events through if they match the predicate
|
||||||
|
filterEvent :: (ArrowChoice cat, Arrow cat) => (a -> Bool) -> Event a `cat` Event a
|
||||||
|
filterEvent predicate = proc ev -> do
|
||||||
|
case ev of
|
||||||
|
Event a | predicate a -> id -< ev
|
||||||
|
_ -> arr (const Tick) -< ()
|
||||||
|
|
||||||
thenA :: (ArrowChoice cat, Arrow cat) => cat a (Either b1 c) -> cat c (Either b1 b2) -> cat a (Either b1 b2)
|
thenA :: (ArrowChoice cat, Arrow cat) => cat a (Either b1 c) -> cat c (Either b1 b2) -> cat a (Either b1 b2)
|
||||||
thenA f g = f >>> arr Left ||| g
|
thenA f g = f >>> arr Left ||| g
|
||||||
|
|
||||||
(>>|) :: (ArrowChoice cat, Arrow cat) => cat a (Either b1 c) -> cat c (Either b1 b2) -> cat a (Either b1 b2)
|
(>>|) :: (ArrowChoice cat, Arrow cat) => cat a (Either b1 c) -> cat c (Either b1 b2) -> cat a (Either b1 b2)
|
||||||
(>>|) = thenA
|
(>>|) = thenA
|
||||||
|
|
||||||
infixl 2 >>|
|
infixl 2 >>||
|
||||||
|
|
||||||
|
(>>||) :: (ArrowChoice cat, Arrow cat) => cat a (Event b) -> cat b c -> cat a (Event c)
|
||||||
|
f >>|| g = proc x -> do
|
||||||
|
e <- f -< x
|
||||||
|
case e of
|
||||||
|
Event e' -> g >>> arr Event -< e'
|
||||||
|
Tick -> arr (const Tick) -< ()
|
||||||
|
|
||||||
|
infixr 3 >>|
|
||||||
|
|
||||||
toEvent :: Mealy eff (Either () a) (Event a)
|
toEvent :: Mealy eff (Either () a) (Event a)
|
||||||
toEvent = arr (either (const Tick) Event)
|
toEvent = arr (either (const Tick) Event)
|
||||||
|
|||||||
+12
-7
@@ -1,3 +1,4 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
module AFRP.Diagram where
|
module AFRP.Diagram where
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Text.Printf as Text
|
import qualified Text.Printf as Text
|
||||||
@@ -23,22 +24,26 @@ emptyDiagram :: DiagramC
|
|||||||
emptyDiagram r = r Empty
|
emptyDiagram r = r Empty
|
||||||
|
|
||||||
|
|
||||||
renderF :: DiagramF (Maybe String) -> Maybe String
|
renderF :: DiagramF (Maybe T.Text) -> Maybe T.Text
|
||||||
renderF = \case
|
renderF = \case
|
||||||
Empty -> Nothing
|
Empty -> Nothing
|
||||||
Step n x -> Just $ T.unpack n <> maybe "" (Text.printf "( %s )") x
|
Step n x -> Just $ n <> T.pack (maybe "" (Text.printf "( %s )") x)
|
||||||
Then (Just a) (Just b) -> Just $ a <> " -> " <> b
|
Then (Just a) (Just b) -> Just $ a <> " -> " <> b
|
||||||
Then (Just a) _ -> Just a
|
Then (Just a) _ -> Just a
|
||||||
Then _ b -> b
|
Then _ b -> b
|
||||||
Both (Just a) (Just b) -> Just $ Text.printf "(%s, %s)" a b
|
Both (Just a) (Just b) -> Just $ T.pack $ Text.printf "(%s, %s)" a b
|
||||||
Both (Just a) _ -> Just a
|
Both (Just a) _ -> Just a
|
||||||
Both _ b -> b
|
Both _ b -> b
|
||||||
Branch (Just a) (Just b) -> Just $ Text.printf "(%s || %s)" a b
|
Branch (Just a) (Just b) -> Just $ T.pack $ Text.printf "(%s || %s)" a b
|
||||||
Branch (Just a) Nothing -> Just $ Text.printf "(%s || <skip>)" a
|
-- I'm leaving this here. This would be technically more correct (showing
|
||||||
|
-- the empty branch, ie. the full if-then-else, but often we care about a
|
||||||
|
-- more simplistic `when` style structure
|
||||||
|
-- Branch (Just a) Nothing -> Just $ T.pack $ Text.printf "(%s || <skip>)" a
|
||||||
|
Branch (Just a) Nothing -> Just $ T.pack $ Text.printf "%s" a
|
||||||
Branch _ b -> b
|
Branch _ b -> b
|
||||||
Merge (Just a) (Just b) -> Just $ Text.printf "(%s, %s)" a b
|
Merge (Just a) (Just b) -> Just $ T.pack $ Text.printf "(%s, %s)" a b
|
||||||
Merge (Just a) _ -> Just a
|
Merge (Just a) _ -> Just a
|
||||||
Merge _ b -> b
|
Merge _ b -> b
|
||||||
Compose (Just a) (Just b) -> Just $ Text.printf "(%s, %s)" a b
|
Compose (Just a) (Just b) -> Just $ T.pack $ Text.printf "(%s, %s)" a b
|
||||||
Compose (Just a) _ -> Just a
|
Compose (Just a) _ -> Just a
|
||||||
Compose _ b -> b
|
Compose _ b -> b
|
||||||
@@ -5,7 +5,7 @@ module HomeAssistant.Controller.Bedroom where
|
|||||||
|
|
||||||
import HomeAssistant.Controller
|
import HomeAssistant.Controller
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import AFRP (Event (..), lMerge, duration, edge)
|
import AFRP (Event (..), lMerge, duration, edge, filterEvent, named, (>>||))
|
||||||
import Data.Aeson (Value, object, (.=))
|
import Data.Aeson (Value, object, (.=))
|
||||||
import Control.Arrow ((>>>), returnA, arr, Arrow (..))
|
import Control.Arrow ((>>>), returnA, arr, Arrow (..))
|
||||||
import Data.Bool (bool)
|
import Data.Bool (bool)
|
||||||
@@ -134,14 +134,16 @@ drawer = entityBool "binary_sensor.bedroom_nightstand_drawer_sensor_masse_contac
|
|||||||
>>> arr (fmap (bool Closed Open))
|
>>> arr (fmap (bool Closed Open))
|
||||||
|
|
||||||
bedroomDrawerController :: HASS (Event Value) ()
|
bedroomDrawerController :: HASS (Event Value) ()
|
||||||
bedroomDrawerController = proc x -> do
|
bedroomDrawerController =
|
||||||
st <- drawer >>> traceEvent -< x
|
drawer >>>
|
||||||
case st of
|
(named "Drawer opened" lightOn <> named "Drawer closed" lightOff)
|
||||||
Event Open -> callService "drawer light on" (switchService [entity] True) -< ()
|
>>> arr (const ())
|
||||||
Event Closed -> callService "drawer light off" (switchService [entity] False) -< ()
|
|
||||||
_ -> returnA -< ()
|
|
||||||
where
|
where
|
||||||
entity = EntityId "switch.bedroom_drawer_light_masse"
|
entity = EntityId "switch.bedroom_drawer_light_masse"
|
||||||
|
lightOff = filterEvent (== Closed)
|
||||||
|
>>|| callService "drawer light off" (switchService [entity] False)
|
||||||
|
lightOn = filterEvent (== Open)
|
||||||
|
>>|| callService "drawer light on" (switchService [entity] True)
|
||||||
|
|
||||||
door :: HASS (Event Value) (Event DoorState)
|
door :: HASS (Event Value) (Event DoorState)
|
||||||
door = entityBool "binary_sensor.makuuhuone_ovi_contact"
|
door = entityBool "binary_sensor.makuuhuone_ovi_contact"
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import Text.Read (readMaybe)
|
|||||||
import UnliftIO.Async
|
import UnliftIO.Async
|
||||||
import Control.Monad.Catch (MonadCatch)
|
import Control.Monad.Catch (MonadCatch)
|
||||||
import Control.Exception.Annotated (checkpoint, Annotation (..))
|
import Control.Exception.Annotated (checkpoint, Annotation (..))
|
||||||
|
import AFRP.Diagram (renderF)
|
||||||
|
|
||||||
step :: (MonadIO m) => FilePath -> T.Text -> UUID -> Auto m a b -> a -> m (b, Auto m a b)
|
step :: (MonadIO m) => FilePath -> T.Text -> UUID -> Auto m a b -> a -> m (b, Auto m a b)
|
||||||
step path name trace st a = do
|
step path name trace st a = do
|
||||||
@@ -122,6 +123,8 @@ defaultMain =
|
|||||||
rrdtool <- fromMaybe "rrdtool" <$> liftIO (lookupEnv "HA_RRDTOOL")
|
rrdtool <- fromMaybe "rrdtool" <$> liftIO (lookupEnv "HA_RRDTOOL")
|
||||||
metricsPort <- lookupPort
|
metricsPort <- lookupPort
|
||||||
writerLimiter <- slidingWindowLimiter rateLimitMetrics 10 20
|
writerLimiter <- slidingWindowLimiter rateLimitMetrics 10 20
|
||||||
|
forM_ controllers $ \(Controller n m) ->
|
||||||
|
logLocM InfoS $ ls $ n <> ": " <> fromMaybe "" (runDiagramC m renderF)
|
||||||
let ents = foldMap (\(Controller _ m) -> entities m) controllers
|
let ents = foldMap (\(Controller _ m) -> entities m) controllers
|
||||||
workers =
|
workers =
|
||||||
[ ("reader", readerAction host 8123 token ents bus),
|
[ ("reader", readerAction host 8123 token ents bus),
|
||||||
|
|||||||
Reference in new issue
Block a user