trial: Trial Data Structure

[ data, data-structure, library, mpl ] [ Propose Tags ]

The Trial Data Structure is a Either-like structure that keeps events history inside. The data type allows to keep track of the Fatality level of each such event entry (Warning or Error).

data Trial e a
           │ │
           │ ╰╴Resulting type
           │
           ╰╴An error item type

    -- | Unsuccessful case
    = Fiasco (DList (Fatality, e))
              │      │         │
              │      │         ╰╴One error item
              │      │
              │      ╰╴Level of damage
              │
              ╰╴Efficient list-container for error type items

    -- | Successful case
    | Result (DList e) a
              │     │  │
              │     │  ╰╴Result
              │     │
              │     ╰╴One warning item
              │
              ╰╴Efficient list-container for warning type items

[Skip to Readme]
Versions [faq] 0.0.0.0
Change log CHANGELOG.md
Dependencies base (>=4.12.0.0 && <4.15), colourista (>=0.1.0.0 && <0.2), dlist (>=0.8.0.8 && <0.9) [details]
License MPL-2.0
Copyright 2020 Kowainik
Author Veronika Romashkina, Dmitrii Kovanikov
Maintainer Kowainik <xrom.xkov@gmail.com>
Category Data Structure, Data
Home page https://github.com/kowainik/trial
Bug tracker https://github.com/kowainik/trial/issues
Source repo head: git clone https://github.com/kowainik/trial.git
Uploaded by vrom911 at 2020-06-21T14:43:59Z
Distributions NixOS:0.0.0.0
Downloads 136 total (52 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs uploaded by user
Build status unknown [no reports yet]

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

For package maintainers and hackage trustees


Readme for trial-0.0.0.0

[back to package description]

trial

GitHub CI Hackage MPL-2.0 license

The Trial Data Structure is a Either-like structure that keeps events history inside. The data type allows to keep track of the Fatality level of each such event entry (Warning or Error).

Project Structure

This is a multi-package project that has the following packages inside:

| Package | Description | |------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| | trial | The main package that contains the Trial data structure, instances and useful functions to work with the structure. | | trial-optparse-applicative | Trial structure integration with the optparse-applicative library for Command Line Interface. | | trial-tomland | Trial structure integration with the tomland library for TOML configurations. | | trial-example | Example project with the usage example of the Trial data structure. |

How to use trial

trial is compatible with the latest GHC versions starting from 8.6.5.

In order to start using trial in your project, you will need to set it up with the three easy steps:

  1. Add the dependency on trial in your project's .cabal file. For this, you should modify the build-depends section by adding the name of this library. After the adjustment, this section could look like this:

    build-depends: base ^>= 4.14
                 , trial ^>= 0.0
    
  2. In the module where you plan to use Trial, you should add the import:

    import Trial (Trial (..), fiasco, prettyPrintTrial)
    
  3. Now you can use the types and functions from the library:

    main :: IO ()
    main = putStrLn $ prettyPrintTrial $ fiasco "This is fiasco, bro!"
    

Trial Data Structure

Let's have a closer look at the Trial data structure. Trial is a sum type that has two constructors:

  • Fiasco — represents the unsuccessful state similar to the Left constructor of Either. However, unlike Left, Fiasco holds a list of all error-like items that happened along the way. Each such item has a notion of Fatality (the severity of the error). The following cases cover Fatality:
    • Error — fatal error that led to the final fatal Fiasco.
    • Warning — non-essential error, which didn't affect the result.
  • Result — represents the successful state similar to the Right constructor of Either. However, unlike Right, Result keeps the list of all error-like items that happened along the way. All error items are warnings as the final result was found anyway.

Schematically, Trial has the following internal representation:

data Trial e a
           │ │
           │ ╰╴Resulting type
           │
           ╰╴An error item type

    -- | Unsuccessful case
    = Fiasco (DList (Fatality, e))
              │      │         │
              │      │         ╰╴One error item
              │      │
              │      ╰╴Level of damage
              │
              ╰╴Efficient list-container for error type items

    -- | Successful case
    | Result (DList e) a
              │     │  │
              │     │  ╰╴Result
              │     │
              │     ╰╴One warning item
              │
              ╰╴Efficient list-container for warning type items

Trial instances

In order to follow the basis idea of the data type, Trial uses smart constructors and different instances to make the structure work the way it works.

Here are the main points:

  • All Fiascos can be created only with the Error Fatality level.
  • The Fatality level can be eased only through the Semigroup appends of different Trials.
  • All error items in Result should have only Warning Fatality level. This is guaranteed by the Trial Semigroup and Applicative instances.
  • Semigroup is responsible for the correct collection of history events, their Fatality level and the final result decision.
  • Semigroup chooses the latest 'Result' and combines all events.
  • Think of Semigroup instance as of high-level combinator of your result.
  • Applicative is responsible for the correct combination of Trials.
  • Applicative returns Fiasco, if at least one value if Fiasco, combine all events.
  • Think of Applicative instance as of low-level combinator of your result on the record fields level.
  • Alternative instance could help when you want to stop on the first Result and get the history of all failures before it.
  • Alternative: return first Result, also combine all events for all Trials before this Result.

Tagged Trial

Additionally, there is a Trial-like data type that has a notion of the tag inside.

The main difference from Trial is that the resulting type contains additional information of the tag (or source it came from). The type looks like this:

type TaggedTrial tag a = Trial tag (tag, a)

Due to the described instances implementation, the tag will always be aligned with the final source it came from.

The library provides different ways to add the tag:

  • Manual with the withTag function
  • Using OverloadedLabelsand the provided IsLabel instance for TaggedTrial.

You can choose the one that is more suitable for your use-case.

Usage Examples

One of the use cases when one could consider using Trial is the configurations in the application.

If you need to collect configurations from different places, combine the results into a single configuration, you can find the Trial data structure quite handy. With trial you can get the event history for free and also you can keep track of where the final result for each component of your configurations type comes from (by using tag functionality).

The complete example in the trial-example package. It combines CLI, TOML configuration and the default options provided in the source code.

| Executable | Description | |------------|-------------| | trial-example | The basic example of config problem with the usage of TaggedTrial | | trial-example-advanced | The basic example of config problem with the usage of TaggedTrial with the Phase based approach. |

To run it you can use the following command:

$ cabal run trial-example
$ cabal run trial-example-advanced

For the successful result you can use the CLI and provide necessary information in order to have the complete configurations:

$ cabal run trial-example -- --host="abc"
$ cabal run trial-example-advanced -- --host="abc"