Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Parallel and Alternative constructors #23

Merged
merged 3 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Add Parallel and Alternative constructors
date: 2023-01-17
context: >
Mealy machine, at least in theory, admit an instance for `Strong` and
`Choice` type classes, to allow parallel and alternative composition.

In our current implementation, we could implement `Strong` and `Choice` just
by forwarding the composition to the `BaseMachine` layer.

Still everytime that we are combining two state machine, either sequentially,
in parallel or in alternative, the set of states is always the cartesian
product of the sets of states. It follows that we would not be able to
understand which constructor we used just by looking at the topology.
decision: >
We decide to add specific constructors for parallel and alternative
composition.
consequences: >
We will be able to keep track of how a state machine is built.

Moreover we will be able to draw not only the set of spaces with allowed
transitions, but also a
[wiring diagram](https://math.mit.edu/~dspivak/informatics/talks/WD-IntroductoryTalk.pdf)
representing the flow of information through the machine.
17 changes: 12 additions & 5 deletions src/CRM/BaseMachine.hs
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,23 @@ instance Functor (ActionResult topology state initialVertex) where
fmap f (ActionResult state output) =
ActionResult state (f output)

-- ** Identity machine
-- ** Stateless machines

-- | The `id` machine always outputs its input and never changes its state
identity :: BaseMachine ('Topology '[ '( '(), '[ '()])]) a a
identity =
-- | The `statelessBase` transforms its input to its output and never changes its state
statelessBase :: (a -> b) -> BaseMachine TrivialTopology a b
marcosh marked this conversation as resolved.
Show resolved Hide resolved
statelessBase f =
BaseMachine
{ initialState = InitialState STuple0
, action = ActionResult
, action = \state input ->
ActionResult state $ f input
}

-- ** Identity machine

-- | The `identity` machine simply outputs its input and never changes its state. It is the result of `statelessBase id`.
identity :: BaseMachine TrivialTopology a a
identity = statelessBase id

-- ** Run a machine

-- | Given an `input`, run the machine to get an output and a new version of
Expand Down
8 changes: 8 additions & 0 deletions src/CRM/Render.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,11 @@ machineAsGraph (Compose machine1 machine2) =
untypedProductGraph
(machineAsGraph machine1)
(machineAsGraph machine2)
machineAsGraph (Parallel machine1 machine2) =
untypedProductGraph
(machineAsGraph machine1)
(machineAsGraph machine2)
machineAsGraph (Alternative machine1 machine2) =
untypedProductGraph
(machineAsGraph machine1)
(machineAsGraph machine2)
37 changes: 29 additions & 8 deletions src/CRM/StateMachine.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module CRM.StateMachine where
import CRM.BaseMachine as BaseMachine
import CRM.Topology
import "base" Control.Category (Category (..))
import "base" Data.Bifunctor (bimap)
import "profunctors" Data.Profunctor (Choice (..), Profunctor (..), Strong (..))
import "singletons-base" Data.Singletons (Demote, SingI, SingKind)

Expand All @@ -25,6 +26,17 @@ data StateMachine input output where
:: StateMachine a b
marcosh marked this conversation as resolved.
Show resolved Hide resolved
-> StateMachine b c
-> StateMachine a c
Parallel
:: StateMachine a b
-> StateMachine c d
-> StateMachine (a, c) (b, d)
Alternative
:: StateMachine a b
-> StateMachine c d
-> StateMachine (Either a c) (Either b d)

stateless :: (a -> b) -> StateMachine a b
stateless f = Basic $ statelessBase f

-- * Category

Expand All @@ -41,32 +53,31 @@ instance Profunctor StateMachine where
lmap :: (a -> b) -> StateMachine b c -> StateMachine a c
lmap f (Basic baseMachine) = Basic $ lmap f baseMachine
lmap f (Compose machine1 machine2) = Compose (lmap f machine1) machine2
lmap f machine = Compose (stateless f) machine


rmap :: (b -> c) -> StateMachine a b -> StateMachine a c
rmap f (Basic baseMachine) = Basic $ rmap f baseMachine
rmap f (Compose machine1 machine2) = Compose machine1 (rmap f machine2)
rmap f machine = Compose machine (stateless f)

-- * Strong

instance Strong StateMachine where
first' :: StateMachine a b -> StateMachine (a, c) (b, c)
first' (Basic baseMachine) = Basic $ first' baseMachine
first' (Compose machine1 machine2) = Compose (first' machine1) (first' machine2)
first' = flip Parallel Control.Category.id

second' :: StateMachine a b -> StateMachine (c, a) (c, b)
second' (Basic baseMachine) = Basic $ second' baseMachine
second' (Compose machine1 machine2) = Compose (second' machine1) (second' machine2)
second' = Parallel Control.Category.id

-- * Choice
-- | An instance of `Choice` allows us to have parallel composition of state machines, meaning that we can pass two inputs to two state machines and get out the outputs of both
instance Choice StateMachine where
left' :: StateMachine a b -> StateMachine (Either a c) (Either b c)
left' (Basic baseMachine) = Basic $ left' baseMachine
left' (Compose machine1 machine2) = Compose (left' machine1) (left' machine2)
left' = flip Alternative Control.Category.id

right' :: StateMachine a b -> StateMachine (Either c a) (Either c b)
right' (Basic baseMachine) = Basic $ right' baseMachine
right' (Compose machine1 machine2) = Compose (right' machine1) (right' machine2)
right' = Alternative Control.Category.id

-- * Run a state machine

Expand All @@ -80,3 +91,13 @@ run (Compose machine1 machine2) a =
(output2, machine2') = run machine2 output1
in
(output2, Compose machine1' machine2')
run (Parallel machine1 machine2) (a, b) =
let
(output1, machine1') = run machine1 a
(output2, machine2') = run machine2 b
in
((output1, output2), Parallel machine1' machine2')
run (Alternative machine1 machine2) a =
case a of
Left a1 -> bimap Left (`Alternative` machine2) $ run machine1 a1
Right a2 -> bimap Right (machine1 `Alternative`) $ run machine2 a2
12 changes: 11 additions & 1 deletion src/CRM/Topology.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

module CRM.Topology where

import "singletons-base" Data.Singletons.Base.TH (singletons)
import "singletons-base" Data.Singletons.Base.TH

-- * Topology

Expand Down Expand Up @@ -59,3 +59,13 @@ instance {-# INCOHERENT #-} AllowedTransition ('Topology map) a b => AllowedTran

instance {-# INCOHERENT #-} AllowedTransition topology a a where
allowsTransition = AllowIdentityEdge

-- ** Trivial topology

-- | The trivial topology only allows identity transitions.
$( singletons
marcosh marked this conversation as resolved.
Show resolved Hide resolved
[d|
trivialTopology :: Topology ()
trivialTopology = Topology []
|]
)