File: README_FULL.md

package info (click to toggle)
ruby-algebrick 0.7.4-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, stretch
  • size: 268 kB
  • ctags: 309
  • sloc: ruby: 1,614; makefile: 3
file content (113 lines) | stat: -rw-r--r-- 3,486 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# Algebrick

Typed structs on steroids based on algebraic types and pattern matching seamlessly integrating with standard Ruby features.

-   Documentation: <http://blog.pitr.ch/algebrick>
-   Source: <https://github.com/pitr-ch/algebrick>
-   Blog: <http://blog.pitr.ch/blog/categories/algebrick/>

## Quick example

{include:file:doc/quick_example.out.rb}

## Algebraic types

Algebraic types are:

-   products with a given number of fields,
-   or a kind of composite type, i.e. a type formed by combining other types.

In Haskell algebraic type looks like this:

    data Tree = Empty
              | Leaf Int
              | Node Tree Tree
              
    depth :: Tree -> Int
    depth Empty = 0
    depth (Leaf n) = 1
    depth (Node l r) = 1 + max (depth l) (depth r)
    
    depth (Node Empty (Leaf 5)) -- => 2

This snippet defines type `Tree` which has 3 possible values:

-  `Empty`
-  `Leaf` with and extra value of type `Int`
-  `Node` with two values of type `Tree` 

and function `depth` to calculate depth of a tree which is called on the last line and evaluates to `2`.

Same `Tree` type and `depth` method can be also defined with this gem as it was shown in {file:README_FULL.md#quick-example Quick Example}.

### Algebrick implementation

Algebrick distinguishes 4 kinds of algebraic types:

1.  **Atom** - type that has only one value, e.g. `Empty`.
2.  **Product** - type that has a set number of fields with given type, e.g. `Leaf(Integer)`
3.  **Variant** - type that is one of the set variants, e.g. `Tree(Empty | Leaf(Integer) | Node(Tree, Tree)`, meaning that values `Empty`, `Leaf[1]`, `Node[Empty, Empry]` have all type `Tree`.
4.  **ProductVariant** - will be created when a recursive type like `List(Empty | List(Integer, List))` is defined. `List` has two variants: `Empty` and itself. Simultaneously it has fields as a product type.

Atom type is implemented with {Algebrick::Atom} and the rest is implemented with {Algebrick::ProductVariant} which behaves differently based on what is set: fields, variants, or both.

More information can be found at <https://en.wikipedia.org/wiki/Algebraic_data_type>.

## Documentation

### Type definition

{include:file:doc/type_def.out.rb}

### Value creation

{include:file:doc/values.out.rb}

### Behaviour extending

{include:file:doc/extending_behavior.out.rb}

### Pattern matching

Pattern matching is implemented with helper objects defined in `ALgebrick::Matchers`.
They use standard `#===` method to match against values.

{include:file:doc/pattern_matching.out.rb}

### Parametrized types

{include:file:doc/parametrized.out.rb}

## What is it good for?

### Defining data structures

<!-- {include:file:doc/data.out.rb} -->

- Simple data structures like trees
- Whenever you find yourself to pass around too many fragile Hash-Array structures

_Examples are coming shortly._

### Serialization

Algebraic types also play nice with JSON serialization and de-serialization making it ideal for defining message-based cross-process communication.

{include:file:doc/json.out.rb}

### Message matching in Actor pattern

Just a small snippet how it can be used in Actor model world.

{include:file:doc/actor.rb}

<!--
### Null Object Pattern

see {http://en.wikipedia.org/wiki/Null_Object_pattern#Ruby}.

{include:file:doc/null.out.rb}

This has advantage over a classical approach that the methods are defined
on one place, no need to track methods in two separate classes `User` and `NullUser`.
-->