haal
A Haskell library for Active Automata Learning.
https://github.com/steve-anunknown/haal#readme
| Stackage Nightly 2026-04-23: | 0.5.0.0 |
| Latest on Hackage: | 0.5.0.0 |
haal-0.5.0.0@sha256:5e583ab14736ab021b404ec2facb93e3df475f82e7a5f362e3a4c309c34c37fb,5240Module documentation for 0.5.0.0
- Haal
- Haal.Automaton
- Haal.BlackBox
- Haal.Dot
- Haal.EquivalenceOracle
- Haal.Experiment
- Haal.Learning
_ _ _ _ _
/ /\ / /\ / /\ / /\ _\ \
/ / / / / / / / \ / / \ /\__ \
/ /_/ / / / / / /\ \ / / /\ \ / /_ \_\
/ /\ \__/ / / / / /\ \ \ / / /\ \ \ / / /\/_/
/ /\ \___\/ / / / / \ \ \ / / / \ \ \ / / /
/ / /\/___/ / / / /___/ /\ \ / / /___/ /\ \ / / /
/ / / / / / / / /_____/ /\ \ / / /_____/ /\ \ / / / ____
/ / / / / / / /_________/\ \ \ / /_________/\ \ \ / /_/_/ ___/\
/ / / / / / / / /_ __\ \_\/ / /_ __\ \_\/_______/\__\/
\/_/ \/_/ \_\___\ /____/_/\_\___\ /____/_/\_______\/
A Haskell library for Active Automata Learning
Haal is an Active Automata Learning library aimed at making it easy to construct learning experiments and explore different configurations of learning algorithms and equivalence oracles. The library is still in its early stages, so nothing is set in stone yet. Most probably, the more features are added the more will the structure of the library change. For the time being, a summary of the current architecture can be found below. Of course, the best documentation is the source code itself.
š§ Features (constantly updated)
| Automaton Type | Learning Algorithms | Equivalence Oracles |
|---|---|---|
| Mealy Machines | ā LM* | ā W-method Oracle |
| ā LM+ | ā Wp-method Oracle | |
| ā Random Words Oracle | ||
| ā Random Walk Oracle | ||
| ā Random W-method Oracle | ||
| ā Random Wp-method Oracle |
Table of Contents
Architecture
The library consists of the following main components:
- Systems under learning
- Automata
- Learning algorithms
- Equivalence oracles
- Experiment
Systems under learning
Systems under learning are defined by the SUL typeclass, parameterized by the types of their inputs and outputs. They must implement functions that allow them to be queried by learning algorithms and equivalence oracles. They are also parameterized by a monad which they operate in. For example, a SUL that is an external program performs IO actions, whereas a SUL that is a pure function operates in the Identity monad.
Automata
Automata are a subclass of systems under learning that also expose information about their internal states. In addition to being queryable, they must implement functions that expose useful structural information, particularly for use by equivalence oracles.
Learning algorithms
Learning algorithms are used to construct hypotheses based on a SUL. They must support initialization, hypothesis construction, and refinement via counterexamples. Not all learning algorithms learn the same type of automaton; the type of automaton to be learned is determined by the algorithm itself. At the moment, all learning algorithms are represented by a typeclass and each specific learning algorithm is meant to be its own data type. The values of the data type are the different configurations of the algorithm.
Equivalence oracles
Equivalence oracles are algorithms that generate a test suite of queries, which are sent to both the current hypothesis and the SUL, in order to discover counterexamples. They must implement functions to report the size of the test suite, generate the suite, and test the hypothesis for counterexamples. As in the case of learning algorithm, equivalence oracles as a whole are represented by a typeclass and a specific equivalence oracle algorithm is meant to be represented by a data type, with the possible values of it being the different configurations of the algorithm.
Experiment
An experiment ties all the components above together. In this library, experiments are represented as values and can be built with various configurations. An experiment is a function that takes a learning algorithm and an oracle, and returns an environment that awaits a SUL to execute the experiment. This is implemented using a Reader monad, where the environment provides access to the SUL.
Installing
The project is available on Hackage. You can check the available version with the command:
stack list haal # cabal list haal
Given that the project is still in its early stages, you probably always want to see the latest version listed.
You can clone the repo and build the library yourself with:
stack build haal:lib
Moreover, given that the library makes use of LiquidHaskell annotations from v0.4.0.1 onwards, you can build with verification turned on:
stack build haal:lib --flag haal:liquid
Example
Hereās a quick GHCi session putting it all together, showing how to define a simple Mealy machine, configure an experiment, and run a learning algorithm using haal. This session is also available as a program examples/demo.hs which can be run with stack runghc examples/demo.hs, or stack run demo and can also be loaded interactively with stack ghci haal:exe:demo.
ghci> :set +m
ghci> import qualified Data.Set as Set
-- Define input, output, and state types
ghci> data Input = A | B deriving (Show, Eq, Ord, Enum, Bounded)
ghci> data Output = X | Y deriving (Show, Eq, Ord, Enum, Bounded)
ghci> data State = S0 | S1 | S2 deriving (Show, Eq, Ord, Enum, Bounded)
-- Define the transition function for the system under learning
ghci> let sulTransitions S0 _ = (S1, X)
ghci| sulTransitions S1 _ = (S2, Y)
ghci| sulTransitions S2 A = (S0, X)
ghci| sulTransitions S2 B = (S0, Y)
ghci> learner = mkLMstar Star
ghci> teacher = either error id (mkWMethod (WMethodConfig 2))
-- Set up the experiment.
ghci> myexperiment = experiment learner teacher
-- Define the Mealy system under learning. Remember that automata can act as suls.
ghci> mysul = mkMealyAutomaton2 sulTransitions (Set.fromList [S0, S1, S2]) S0
-- Run the experiment
ghci> (learnedmodel, stats) = runExperiment myexperiment mysul
-- View the learned model
ghci> learnedmodel
{
Current State: 0,
Initial State: 0,
Transitions: fromList [
((0,A),(1,X)),
((0,B),(1,X)),
((1,A),(2,Y)),
((1,B),(2,Y)),
((2,A),(0,X)),
((2,B),(0,Y))
]
}
This shows how a simple Mealy machine can be learned using the LM* algorithm and a W-method equivalence oracle.
This also showcases some strong points of using a functional programming language like haskell for the task of active automata learning:
1. Type-safe alphabets
In Haal, the input and output alphabets are represented as plain Haskell data types. This means the compiler can catch errors early ā for example, if a symbol not defined in the alphabet accidentally appears in a transition, the type checker will reject the code. This eliminates entire classes of bugs that are easy to make in more loosely typed implementations.
data Input = A | B deriving (Show, Eq, Ord, Enum, Bounded)
data Output = X | Y deriving (Show, Eq, Ord, Enum, Bounded)
2. Automata as functions
Instead of defining transitions via tables or external formats like dot, Haskell allows transitions to be encoded as pure functions:
sulTransitions :: State -> Input -> (State, Output)
sulTransitions S0 _ = (S1, X)
sulTransitions S1 _ = (S2, Y)
sulTransitions S2 A = (S0, X)
sulTransitions S2 B = (S0, Y)
Combined with exhaustive pattern matching and totality checking, this ensures that:
- All input cases are handled for each state
- No states or transitions are forgotten
- The definition is both human-readable and machine-checkable
In essence, Haskell itself is the language for defining automata, without needing external DSLs or formats like DOT.
Changes
Changelog for haal
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to the Haskell Package Versioning Policy.
Unreleased
0.5.0.0 - 2026-04-22
Fixed
Haal.Learning.LMstar.equivalenceClassesnow iterates overSmonly, matching Definition 2 of Shahbaz & Groz, āInferring Mealy Machinesā. Previously it iterated overSm āŖ SmĀ·I, which could pick a representative fromSmĀ·Iwhoserep++[i]was not in the observation table, causingmakeHypothesisto fail on many real protocol models (DTLS, medium MQTT) with"invariant violation ā makeHypothesis failed on closed consistent table".
Changed (breaking)
Haal.Learning.LMstar.otIsConsistenttightens its output constraint fromEq otoOrd oto support Map-based row grouping.
Changed
Haal.Learning.LMstar.otIsConsistentgroups prefixes by row signature before pairwise comparison, reducing the worst-case pair enumeration.Haal.Dot.parseDotusesSet.fromListinstead ofnubfor deduplication (O(n²) ā O(n log n)).
Added
tasty-bench-based benchmark suite inbench/covering BlackBox operations, W-method / Wp-method test-suite generation, DOT serialize/parse/roundtrip, and end-to-end learning experiments. Run withstack bench haal.
0.4.1.0 - 2026-03-21
Added
- Serializer from Mealy Automaton to Dot format.
- haal-gen executable that accepts a .dot file and produces a haskell module that exports a function for the specified Mealy Automaton, with specific input and output types, rather than just using String for both.
- haal-models subpackage that exports learned models of tls, mqtt, tcp and dtls protocols.
0.4.0.2 - 2026-03-17
Changed
- Dependency bounds.
0.4.0.1 - 2026-03-17
Added
- Optional
liquidCabal flag (--flag haal:liquid) to enable LiquidHaskell verification without requiring it as a dependency for normal builds.
Verified
Haal.BlackBox:walkproduces outputs of length equal to the input length.Haal.Learning.LMstar:ObservationTableinvariant that all entries inmappingTmap to non-empty output lists, preserved acrossupdateMap,makeConsistent,makeClosed, andinitializeOT.
0.4.0.0 - 2026-03-16
Changed
- Changed the types of oraclesā constructors from
<Oracle>toEither String <Oracle>whereStringis an error message indicating invalid values to<OracleConfig>. Now, for example, instead oforacle = mkWMethod (WMethodConfig 2), one should either pattern match withcaseor dooracle = either error id (mkWMethod (WMethodConfig 2)).
0.3.0.0 - 2026-03-11
Changed
SULtypeclass no longer takesiandoas class parameters; they are now universally quantified in the method signatures. Instances should dropi ofrom their instance heads:instance SUL MyType IOinstead ofinstance SUL MyType IO Input Output.Automatontypeclass likewise dropsiandofrom its class head. All constraint occurrences(Automaton aut s i o)become(Automaton aut s).
0.2.0.0 - 2026-03-05
Added
stepPure,walkPure,resetPureexported fromHaal.BlackBoxConfigrecord types for all equivalence oracles:WMethodConfig,WpMethodConfig,RandomWalkConfig,RandomWordsConfig,RandomWMethodConfig,RandomWpMethodConfigmkCombinedOraclesmart constructor forCombinedOraclerandomWordsConfigaccessor forRandomWordsmealyDelta,mealyLambdaas explicit named exports fromHaal.Automaton.MealyAutomaton
Changed
- All oracle constructors now take a
Configrecord instead of positional arguments:mkWMethod :: WMethodConfig -> WMethod,mkWpMethod :: WpMethodConfig -> WpMethod, etc. mkRandomWMethodandmkRandomWpMethodnow take aConfigrecord instead of positional arguments (also fixes an argument-order bug in the old interface)
Removed
mealyStepfromHaal.Automaton.MealyAutomaton; usestepPurefromHaal.BlackBoxmooreStepfromHaal.Automaton.MooreAutomaton; usestepPurefromHaal.BlackBox- Raw constructor exports (
MealyAutomaton (..),WMethod (..),WpMethod (..),RandomWalk (..),RandomWords (..),CombinedOracle (..),LMstar (..)); use the correspondingmk-prefixed smart constructors instead
0.1.0.0 - 2025-12-02
- Initial release of
haal.- Support for Mealy Automata and DFAs.
- One learner for Mealy Automata and DFAs with 2 configurations.
- LStar.
- LPlus.
- Basic equivalence oracles.
- Examples that showcase usage of the library.