Skip to content

Commit

Permalink
implement unrestrictedMachine
Browse files Browse the repository at this point in the history
add example of an unrestricted machine with boolean state
  • Loading branch information
Marco Perone committed Jan 26, 2023
1 parent e8e04bf commit 2273f57
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 1 deletion.
25 changes: 25 additions & 0 deletions spec/CRM/Example/BooleanStateMachine.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{-# LANGUAGE GADTs #-}

module CRM.Example.BooleanStateMachine where

import "crm" CRM.BaseMachine
import "crm" CRM.StateMachine
import "singletons-base" Data.Singletons.Base.TH

booleanStateMachine
:: forall a
. SBool a
-> StateMachine Int Int
booleanStateMachine initialState =
unrestrictedMachine @Bool
( \state input -> case state of
SFalse ->
if even input
then ActionResult SFalse (input + 1)
else ActionResult STrue (input * 3)
STrue ->
if even input
then ActionResult STrue (input - 1)
else ActionResult SFalse (input * 5)
)
(InitialState initialState)
15 changes: 14 additions & 1 deletion spec/CRM/StateMachineSpec.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module CRM.StateMachineSpec where

import CRM.Example.BooleanStateMachine (booleanStateMachine)
import CRM.Example.LockDoor
import CRM.Example.Switch (switchMachine)
import "crm" CRM.StateMachine
Expand All @@ -8,7 +9,6 @@ import "hspec" Test.Hspec (Expectation, Spec, describe, it, shouldBe)

shouldOutput :: (Eq b, Show b) => (b, StateMachine a b) -> b -> Expectation
shouldOutput (output, _) expectedOutput = output `shouldBe` expectedOutput

spec :: Spec
spec =
describe "StateMachine" $ do
Expand Down Expand Up @@ -66,3 +66,16 @@ spec =
let
runOnce = snd $ run (Basic $ lockDoorMachine SIsLockLocked) LockUnlock
run runOnce LockOpen `shouldOutput` LockOpened

describe "boolean state machine" $ do
it "outputs 1 when it is in a False state and receives a 0" $ do
run (booleanStateMachine SFalse) 0 `shouldOutput` 1

it "outputs 3 when it is in a False state and receives a 1" $ do
run (booleanStateMachine SFalse) 1 `shouldOutput` 3

it "outputs -1 when it is in a True state and receives a 0" $ do
run (booleanStateMachine STrue) 0 `shouldOutput` (-1)

it "outputs 5 when it is in a True state and receives a 1" $ do
run (booleanStateMachine STrue) 1 `shouldOutput` 5
13 changes: 13 additions & 0 deletions src/CRM/BaseMachine.hs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,19 @@ statelessBase f =
identity :: BaseMachine TrivialTopology a a
identity = statelessBase id

-- ** Unfold

-- | a machine modelled with explicit state, where every transition is allowed
unrestrictedBaseMachine
:: (forall initialVertex. state initialVertex -> a -> ActionResult (AllowAllTopology @vertex) state initialVertex b)
-> InitialState (state :: vertex -> Type)
-> BaseMachine (AllowAllTopology @vertex) a b
unrestrictedBaseMachine action initialState =
BaseMachine
{ initialState = initialState
, action = action
}

-- ** Run a machine

-- | Given an `input`, run the machine to get an output and a new version of
Expand Down
15 changes: 15 additions & 0 deletions src/CRM/StateMachine.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import CRM.BaseMachine as BaseMachine
import CRM.Topology
import "base" Control.Category (Category (..))
import "base" Data.Bifunctor (bimap)
import "base" Data.Kind (Type)
import "profunctors" Data.Profunctor (Choice (..), Profunctor (..), Strong (..))
import "singletons-base" Data.Singletons (Demote, SingI, SingKind)
import Prelude hiding ((.))

-- | A `StateMachine` is a [Mealy machine](https://en.wikipedia.org/wiki/Mealy_machine)
-- with inputs of type `input` and outputs of type `output`
Expand All @@ -35,9 +37,22 @@ data StateMachine input output where
-> StateMachine c d
-> StateMachine (Either a c) (Either b d)

-- | a state machine which does not rely on state
stateless :: (a -> b) -> StateMachine a b
stateless f = Basic $ statelessBase f

-- | a machine modelled with explicit state, where every transition is allowed
unrestrictedMachine
:: (Demote vertex ~ vertex, SingKind vertex, SingI (AllowAllTopology @vertex), Show vertex)
=> ( forall initialVertex
. state initialVertex
-> a
-> ActionResult (AllowAllTopology @vertex) state initialVertex b
)
-> InitialState (state :: vertex -> Type)
-> StateMachine a b
unrestrictedMachine action state = Basic $ unrestrictedBaseMachine action state

-- * Category

instance Category StateMachine where
Expand Down
10 changes: 10 additions & 0 deletions src/CRM/Topology.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
module CRM.Topology where

import "singletons-base" Data.Singletons.Base.TH
import "singletons-base" Prelude.Singletons

-- * Topology

Expand Down Expand Up @@ -68,3 +69,12 @@ $( singletons
trivialTopology = Topology []
|]
)

-- ** Allow all topology

$( singletons
[d|
allowAllTopology :: (Bounded a, Enum a) => Topology a
allowAllTopology = Topology [(a, [minBound .. maxBound]) | a <- [minBound .. maxBound]]
|]
)

0 comments on commit 2273f57

Please sign in to comment.