Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kowainik/trial
⚖️ Trial Data Type
https://github.com/kowainik/trial
data-structures hacktoberfest haskell haskell-library
Last synced: about 1 month ago
JSON representation
⚖️ Trial Data Type
- Host: GitHub
- URL: https://github.com/kowainik/trial
- Owner: kowainik
- License: mpl-2.0
- Created: 2020-05-22T17:36:59.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2024-05-11T16:52:51.000Z (8 months ago)
- Last Synced: 2024-05-11T17:49:39.785Z (8 months ago)
- Topics: data-structures, hacktoberfest, haskell, haskell-library
- Language: Haskell
- Homepage:
- Size: 88.9 KB
- Stars: 20
- Watchers: 5
- Forks: 8
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
# trial
[![GitHub CI](https://github.com/kowainik/trial/workflows/CI/badge.svg)](https://github.com/kowainik/trial/actions)
[![Hackage](https://img.shields.io/hackage/v/trial.svg?logo=haskell)](https://hackage.haskell.org/package/trial)
[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](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`](https://hackage.haskell.org/package/optparse-applicative) library for Command Line Interface. |
| `trial-tomland` | `Trial` structure integration with the [`tomland`](https://hackage.haskell.org/package/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:```haskell
build-depends: base ^>= 4.14
, trial ^>= 0.0
```2. In the module where you plan to use `Trial`, you should add the import:
```haskell
import Trial (Trial (..), fiasco, prettyPrintTrial)
```3. Now you can use the types and functions from the library:
```haskell
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:
```haskell
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 `Fiasco`s can be created only with the `Error` `Fatality` level.
* The `Fatality` level can be eased only through the `Semigroup` appends of
different `Trial`s.
* 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 `Trial`s.
* `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 `Trial`s 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:```haskell
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 `OverloadedLabels`and 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`](https://github.com/kowainik/trial/blob/main/trial-example/app/Main.hs) | The basic example of config problem with the usage of `TaggedTrial` |
| [`trial-example-advanced`](https://github.com/kowainik/trial/blob/main/trial-example/app-advanced/Main.hs) | 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:
```shell
$ 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:```shell
$ cabal run trial-example -- --host="abc"
$ cabal run trial-example-advanced -- --host="abc"
```