Search documentation of Finitomata

# View Source Finitomata.Transition (Finitomata v0.22.1)

The internal representation of `Transition`.

It includes `from` and `to` states, and the `event`, all represented as atoms.

# Summary

## Types

event()

The event in FSM

event_kind()

The kind of event

state()

The state of FSM

t()

The transition is represented by `from` and `to` states and the `event`.

## Functions

allowed(transitions, options \\ [])

Returns the list of all the transitions, matching the options.

allowed(transitions, from, event)

Returns the list of all the transitions, matching the `from` state and the `event`.

allowed?(transitions, from, to)

Returns `true` if the transition `from` → `to` is allowed, `false` otherwise.

ambiguous(transitions)

Returns keyword list of `{Finitomata.Transition.state(), [Finitomata.Transition.event()]}` for transitions which do not have a determined to state.

continuation(states \\ :states, from, transitions)

Returns the continuation from the state given which inevitably lead to other state(s).

determined(transitions)

Returns keyword list of `{Finitomata.Transition.state(), Finitomata.Transition.event()}` tuples for determined transition from the current state.

determined(transitions, state)

Returns `{:ok, {event(), state()}}` tuple if there is a determined transition from the current state, `:error` otherwise.

entry(what \\ :state, transitions)

Returns the state after starting one, so-called `entry` state.

event_kind(event)

Returns the kind of event.

events(transitions, purge_internal \\ false)

Returns the not ordered list of events, including or excluding the internal starting and ending transitions `:__start__` and `__end__` according to the second argument.

exit(what \\ :states, transitions)

Returns the states before ending one, so-called `exit` states.

exiting(what \\ :states, transitions)

Returns all the states which inevitably lead to the ending one.

hard(states \\ :states, transitions)

Returns all the hard transitions which inevitably lead to the next state (events ending with an exclamation sign,) which makes the FSM to go to the next state with `:continue` callback.

loops(what \\ :states, transitions)

Returns all the loops aka internal paths where starting and ending states are the same one.

paths(what \\ :states, transitions, from \\ :*, to \\ :*)

Returns all the paths from starting to ending state.

responds?(transitions, from, event)

Returns `true` if the state `from` hsa an outgoing transition with `event`, false otherwise.

states(transitions, purge_internal \\ false)

Returns the not ordered list of states, including or excluding the starting and ending states `:*` according to the second argument.

# Types

Link to this type

# event()

View Source

    @type event() :: atom()

The event in FSM

Link to this type

# event_kind()

View Source

    @type event_kind() :: :soft | :hard | :normal

The kind of event

Link to this type

# state()

View Source

    @type state() :: atom()

The state of FSM

Link to this type

# t()

View Source

    @type t() :: %Finitomata.Transition{
      from: state(),
      to: state() | [state()],
      event: event()
    }

The transition is represented by `from` and `to` states and the `event`.

# Functions

Link to this function

# allowed(transitions, options \\ [])

View Source

    @spec allowed([t()],
      from: state(),
      to: state(),
      with: event(),
      as: :states | :transitions
    ) :: [
      {state(), state(), event()}
    ]

Returns the list of all the transitions, matching the options.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "idle --> |to_s1| s1\n" <>
    ...>     "s1 --> |to_s2| s2\n" <>
    ...>     "s1 --> |to_s3| s3\n" <>
    ...>     "s2 --> |to_s3| s3")
    ...> Finitomata.Transition.allowed(transitions, to: [:idle, :*])
    [{:*, :idle, :__start__}, {:s3, :*, :__end__}]
    iex> Finitomata.Transition.allowed(transitions, from: :s1)
    [{:s1, :s2, :to_s2}, {:s1, :s3, :to_s3}]
    iex> Finitomata.Transition.allowed(transitions, from: :s1, to: :s3)
    [{:s1, :s3, :to_s3}]
    iex> Finitomata.Transition.allowed(transitions, from: :s1, with: :to_s3)
    [{:s1, :s3, :to_s3}]
    iex> Finitomata.Transition.allowed(transitions, from: :s2, with: :to_s2)
    []

Link to this function

# allowed(transitions, from, event)

View Source

    @spec allowed([t()], state(), event()) :: [state()]

Returns the list of all the transitions, matching the `from` state and the `event`.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.allowed(transitions, :s1, :foo)
    [:s2]
    ...> Finitomata.Transition.allowed(transitions, :s1, :*)
    []

Link to this function

# allowed?(transitions, from, to)

View Source

    @spec allowed?([t()], state(), state()) :: boolean()

Returns `true` if the transition `from` → `to` is allowed, `false` otherwise.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.allowed?(transitions, :s1, :s2)
    true
    ...> Finitomata.Transition.allowed?(transitions, :s1, :*)
    false

Link to this function

# ambiguous(transitions)

View Source

    @spec ambiguous([t()]) :: [{state(), {event(), state()}}]

Returns keyword list of `{Finitomata.Transition.state(), [Finitomata.Transition.event()]}` for transitions which do not have a determined to state.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "idle --> |to_s1| s1\n" <>
    ...>     "s1 --> |to_s2| s2\n" <>
    ...>     "s1 --> |to_s3| s3\n" <>
    ...>     "s2 --> |to_s1| s3\n" <>
    ...>     "s2 --> |ambiguous| s3\n" <>
    ...>     "s2 --> |ambiguous| s4\n" <>
    ...>     "s3 --> |determined| s3\n" <>
    ...>     "s3 --> |determined| s4\n")
    ...> Finitomata.Transition.ambiguous(transitions)
    [s3: {:determined, [:s3, :s4]}, s2: {:ambiguous, [:s3, :s4]}]

Link to this function

# continuation(states \\ :states, from, transitions)

View Source

    @spec continuation(:states | :transitions, state(), [t()]) ::
      nil | Finitomata.Transition.Path.t() | [t()]

Returns the continuation from the state given which inevitably lead to other state(s).

All the transitions from this state are hard (ending with `!`,) which makes the FSM to go through all these states in `:continue` callbacks.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : start\nentry --> exit : go!\nexit --> done : finish\ndone --> [*] : terminate")
    ...> Finitomata.Transition.continuation(:entry, Finitomata.Transition.hard(:transitions, transitions))
    %Finitomata.Transition.Path{from: :entry, to: :exit, path: [go!: :exit]}

Link to this function

# determined(transitions)

View Source

    @spec determined([t()]) :: [{state(), {event(), state()}}]

Returns keyword list of `{Finitomata.Transition.state(), Finitomata.Transition.event()}` tuples for determined transition from the current state.

The transition is determined, if it is the only transition allowed from the state.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "idle --> |to_s1| s1\n" <>
    ...>     "s1 --> |to_s2| s2\n" <>
    ...>     "s1 --> |to_s3| s3\n" <>
    ...>     "s2 --> |to_s1| s3\n" <>
    ...>     "s2 --> |ambiguous| s3\n" <>
    ...>     "s2 --> |ambiguous| s4\n" <>
    ...>     "s3 --> |determined| s3\n" <>
    ...>     "s3 --> |determined| s4\n")
    ...> Finitomata.Transition.determined(transitions)
    [s4: :__end__, s3: :determined, idle: :to_s1]

Link to this function

# determined(transitions, state)

View Source

    @spec determined([t()], state()) :: {:ok, {event(), state()}} | :error

Returns `{:ok, {event(), state()}}` tuple if there is a determined transition from the current state, `:error` otherwise.

The transition is determined, if it is the only transition allowed from the state.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "idle --> |to_s1| s1\n" <>
    ...>     "s1 --> |to_s2| s2\n" <>
    ...>     "s1 --> |to_s3| s3\n" <>
    ...>     "s2 --> |to_s3| s3")
    ...> Finitomata.Transition.determined(transitions, :s1)
    :error
    iex> Finitomata.Transition.determined(transitions, :s2)
    {:ok, {:to_s3, :s3}}
    iex> Finitomata.Transition.determined(transitions, :s3)
    {:ok, {:__end__, :*}}

Link to this function

# entry(what \\ :state, transitions)

View Source

    @spec entry(:state | :transition, [t()]) :: state()

Returns the state after starting one, so-called `entry` state.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : foo\nentry --> exit : go\nexit --> [*] : terminate")
    ...> Finitomata.Transition.entry(transitions)
    :entry

Link to this function

# event_kind(event)

View Source

    @spec event_kind(event() | t()) :: event_kind()

Returns the kind of event.

If event ends up with an exclamation sign, it’s `:hard`, meaning the respective transition would be initiated automatically when the `from` state of such a transition is reached.

If event ends up with a question mark, it’s `:soft`, meaning no error would have been reported in a case transition fails.

Otherwise the event is `:normal`.

    iex> {:ok, [_, hard, _]} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : foo\nentry --> exit : go!\nexit --> [*] : terminate")
    ...> Finitomata.Transition.event_kind(hard)
    :hard

Link to this function

# events(transitions, purge_internal \\ false)

View Source

    @spec events([t()], boolean()) :: [state()]

Returns the not ordered list of events, including or excluding the internal starting and ending transitions `:__start__` and `__end__` according to the second argument.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse("s1 --> |ok| s2\ns1 --> |ko| s3")
    ...> Finitomata.Transition.events(transitions, true)
    [:ok, :ko]
    ...> Finitomata.Transition.events(transitions)
    [:__start__, :ok, :ko, :__end__]

Link to this function

# exit(what \\ :states, transitions)

View Source

    @spec exit(:states | :transitions, [t()]) :: [state()]

Returns the states before ending one, so-called `exit` states.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "entry --> |process| processing\nprocessing --> |ok| success\nprocessing --> |ko| error"
    ...>   )
    ...> Finitomata.Transition.exit(transitions)
    [:error, :success]

Link to this function

# exiting(what \\ :states, transitions)

View Source

    @spec exiting(:states | :transitions, [t()]) ::
      Enumerable.t([t()]) | [Finitomata.Transition.Path.t()]

Returns all the states which inevitably lead to the ending one.

All the transitions from these states to the ending one are hard (ending with `!`,) which makes the FSM to go through all these states in `:continue` callbacks.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : start\nentry --> exit : go!\nexit --> [*] : terminate")
    ...> Finitomata.Transition.exiting(transitions)
    [%Finitomata.Transition.Path{from: :entry, to: :*, path: [go!: :exit, terminate: :*]}]

Link to this function

# hard(states \\ :states, transitions)

View Source

    @spec hard(:states | :transitions, [t()]) ::
      Enumerable.t(t()) | [Finitomata.Transition.Path.t()]

Returns all the hard transitions which inevitably lead to the next state (events ending with an exclamation sign,) which makes the FSM to go to the next state with `:continue` callback.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : start\nentry --> exit : go!\nexit --> [*] : terminate")
    ...> Finitomata.Transition.hard(transitions)
    [entry: :go!]
    ...> Finitomata.Transition.hard(:transitions, transitions)
    [%Finitomata.Transition{from: :entry, to: :exit, event: :go!}]

Link to this function

# loops(what \\ :states, transitions)

View Source

    @spec loops(:states | :transitions, [t()]) ::
      Enumerable.t(t()) | [Finitomata.Transition.Path.t()]

Returns all the loops aka internal paths where starting and ending states are the same one.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> s1 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.loops(transitions)
    [%Finitomata.Transition.Path{from: :s1, to: :s1, path: [ok: :s2, ok: :s1]},
     %Finitomata.Transition.Path{from: :s2, to: :s2, path: [ok: :s1, ok: :s2]}]

Link to this function

# paths(what \\ :states, transitions, from \\ :*, to \\ :*)

View Source

    @spec paths(:states | :transitions, [t()], state(), state()) ::
      Enumerable.t(t()) | [Finitomata.Transition.Path.t()]

Returns all the paths from starting to ending state.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns1 --> s3 : ok\ns2 --> [*] : ko\ns3 --> [*] : ko")
    ...> Finitomata.Transition.paths(transitions)
    [%Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s2, ko: :*]},
     %Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s3, ko: :*]}]

Link to this function

# responds?(transitions, from, event)

View Source

    @spec responds?([t()], state(), event()) :: boolean()

Returns `true` if the state `from` hsa an outgoing transition with `event`, false otherwise.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.responds?(transitions, :s1, :ok)
    true
    ...> Finitomata.Transition.responds?(transitions, :s1, :ko)
    false

Link to this function

# states(transitions, purge_internal \\ false)

View Source

    @spec states([t()], boolean()) :: [state()]

Returns the not ordered list of states, including or excluding the starting and ending states `:*` according to the second argument.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.states(transitions, true)
    [:s1, :s2]
    ...> Finitomata.Transition.states(transitions)
    [:*, :s1, :s2]