// @flow
// based on State Machines All The Way Down
// An Architecture for Dependently Typed Applications
// https://eb.host.cs.st-andrews.ac.uk/drafts/states-all-the-way.pdf
// by Edwin Brady
//
// finite state machine
//
// By defining this state machine in a type, we can ensure that any sequence
// of operations which type checks is a valid sequence of operations on a door.
// The door can be open or closed
class DoorOpen {}
class DoorClosed {}
type DoorState = DoorOpen | DoorClosed;
//
// operations
//
// A represents the return type of the operation
// I represents the input state (the precondition)
// O represents output state (the postcondition)
class Operation<A, I: DoorState, O: DoorState> {
a: A;
constructor(a: A) {
this.a = a
}
}
class Open extends Operation<void, DoorClosed, DoorOpen> {}
class Close extends Operation<void, DoorOpen, DoorClosed> {}
class RingBell extends Operation<void, DoorClosed, DoorClosed> {}
// monadic bind
function chain<A, S1: DoorState, S2: DoorState, S3: DoorState, B>(f: (a: A) => Operation<B, S2, S3>, fa: Operation<A, S1, S2>): Operation<B, S1, S3> {
return new Operation(f(fa.a).a)
}
//
// examples
//
// if you open the door, you must close it
const x1: Operation<void, DoorClosed, DoorClosed> = new Open()
// ok
const x2: Operation<void, DoorClosed, DoorClosed> = chain(
() => new Close(),
new Open()
)
// you can't ring the bell when the door is open
const x3: Operation<void, DoorClosed, DoorClosed> = chain(
() => chain(
() => new Close(),
new RingBell()
),
new Open()
)
// ok
const x4: Operation<void, DoorClosed, DoorClosed> = chain(
() => chain(
() => new RingBell(),
new Close()
),
new Open()
)
Blog post: Phantom types with Flow