Skip to content

Instantly share code, notes, and snippets.

@gcanti
Last active April 6, 2018 19:34
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gcanti/07984b1f003e573e265cd896784c8b64 to your computer and use it in GitHub Desktop.
Save gcanti/07984b1f003e573e265cd896784c8b64 to your computer and use it in GitHub Desktop.
Type level programming with Flow, encoding a finite state machine
// @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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment