algebrick 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -7
- data/README_FULL.md +53 -31
- data/VERSION +1 -1
- data/lib/algebrick.rb +33 -54
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3e1690d46e7e9d3c9332eed298a46d35ec628ce
|
4
|
+
data.tar.gz: 7cf4ccef5ae69a055e9ac7806b84e0a6724d7c1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae2f4b9cd4bebbb889b8d91383cbe9380d8b2c440104c0302b4b128b589fbdb6d1bdae886a49cf790022a76f1fe304180bc8b6ed77da6ce60d6aba038fac67fd
|
7
|
+
data.tar.gz: becadc540cc08d5002060e0fc1e64eac87d1848ff11bc44b6e628a45b61bc705fcce870741e568b2e6803e39b08b7f789d42e2926d176f8c40c8083bea3db21c
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/pitr-ch/algebrick.png?branch=master)](https://travis-ci.org/pitr-ch/algebrick)
|
4
4
|
|
5
|
-
It's a
|
5
|
+
It's a gem providing **algebraic types** and **pattern matching** seamlessly integrates with standard features Ruby.
|
6
6
|
|
7
7
|
- Documentation: <http://blog.pitr.ch/algebrick>
|
8
8
|
- Source: <https://github.com/pitr-ch/algebrick>
|
@@ -27,24 +27,24 @@ end
|
|
27
27
|
```
|
28
28
|
|
29
29
|
Now types `Tree(Empty | Leaf | Node)`, `Empty`, `Leaf(Integer)` and `Node(Tree, Tree)` are defined.
|
30
|
-
|
30
|
+
Let's add a method, don't miss the **pattern matching** example.
|
31
31
|
|
32
32
|
```ruby
|
33
33
|
module Tree
|
34
34
|
# compute depth of a tree
|
35
35
|
def depth
|
36
36
|
match self,
|
37
|
-
Empty
|
38
|
-
Leaf
|
37
|
+
(on Empty, 0),
|
38
|
+
(on Leaf, 1),
|
39
39
|
# ~ will store and pass matched parts to variables left and right
|
40
|
-
Node.(~any, ~any)
|
40
|
+
(on Node.(~any, ~any) do |left, right|
|
41
41
|
1 + [left.depth, right.depth].max
|
42
|
-
end
|
42
|
+
end)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
```
|
46
46
|
|
47
|
-
|
47
|
+
Method defined in module `Tree` are passed down to **all** values of type Tree
|
48
48
|
|
49
49
|
```ruby
|
50
50
|
Empty.depth # => 0
|
data/README_FULL.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Algebrick
|
2
2
|
|
3
|
-
|
3
|
+
It's a gem providing **algebraic types** and **pattern matching** and seamlessly integrating with standard features of Ruby.
|
4
4
|
|
5
5
|
- Documentation: <http://blog.pitr.ch/algebrick>
|
6
6
|
- Source: <https://github.com/pitr-ch/algebrick>
|
@@ -10,30 +10,46 @@ Is a small gem providing **algebraic types** and **pattern matching** on them fo
|
|
10
10
|
|
11
11
|
{include:file:doc/quick_example.out.rb}
|
12
12
|
|
13
|
-
## Algebraic types
|
13
|
+
## Algebraic types
|
14
14
|
|
15
|
-
|
15
|
+
Algebraic type is a kind of composite type, i.e. a type formed by combining other types.
|
16
|
+
In Haskell algebraic type looks like this:
|
16
17
|
|
17
18
|
data Tree = Empty
|
18
19
|
| Leaf Int
|
19
20
|
| Node Tree Tree
|
21
|
+
|
22
|
+
depth :: Tree -> Int
|
23
|
+
depth Empty = 0
|
24
|
+
depth (Leaf n) = 1
|
25
|
+
depth (Node l r) = 1 + max (depth l) (depth r)
|
26
|
+
|
27
|
+
depth (Node Empty (Leaf 5)) -- => 2
|
20
28
|
|
21
|
-
|
22
|
-
`Empty`, `Leaf` with and extra value of type `Int`, `Node` with two values of type `Tree`.
|
23
|
-
See {https://en.wikipedia.org/wiki/Algebraic_data_type}.
|
29
|
+
This snippet defines type `Tree` which has 3 possible values:
|
24
30
|
|
25
|
-
|
31
|
+
- `Empty`
|
32
|
+
- `Leaf` with and extra value of type `Int`
|
33
|
+
- `Node` with two values of type `Tree`
|
26
34
|
|
27
|
-
|
35
|
+
and function `depth` to calculate depth of a tree which is called on the last line and evaluates to `2`.
|
28
36
|
|
29
|
-
|
37
|
+
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}.
|
30
38
|
|
31
|
-
|
32
|
-
2. **Product** a type that has a set number of fields with given type e.g. `Leaf(Integer)`
|
33
|
-
3. **Variant** a type that does have set number of variants e.g. `Tree(Empty | Leaf(Integer) | Node(Tree, Tree)`. It means that values of `Empty`, `Leaf[1]`, `Node[Empty, Empry]` are all of type `Tree`.
|
34
|
-
4. **ProductVariant** will be created when a recursive type like `List(Empty | List(Integer, List))` is defined. `List` has two variants `Empty` and itself, and simultaneously it has fields as product type.
|
39
|
+
### Algebrick implementation
|
35
40
|
|
36
|
-
|
41
|
+
Algebrick distinguishes 4 kinds of algebraic types:
|
42
|
+
|
43
|
+
1. **Atom** - type that has only one value, e.g. `Empty`.
|
44
|
+
2. **Product** - type that has a set number of fields with given type, e.g. `Leaf(Integer)`
|
45
|
+
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`.
|
46
|
+
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.
|
47
|
+
|
48
|
+
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.
|
49
|
+
|
50
|
+
More information can be found at <https://en.wikipedia.org/wiki/Algebraic_data_type>.
|
51
|
+
|
52
|
+
## Documentation
|
37
53
|
|
38
54
|
### Type definition
|
39
55
|
|
@@ -43,14 +59,14 @@ Atom type is implemented with {Algebrick::Atom} and the rest is implemented with
|
|
43
59
|
|
44
60
|
{include:file:doc/values.out.rb}
|
45
61
|
|
46
|
-
###
|
62
|
+
### Behaviour extending
|
47
63
|
|
48
64
|
{include:file:doc/extending_behavior.out.rb}
|
49
65
|
|
50
66
|
### Pattern matching
|
51
67
|
|
52
|
-
|
53
|
-
`#===` method
|
68
|
+
Pattern matching is implemented with helper objects defined in `ALgebrick::Matchers`.
|
69
|
+
They use standard `#===` method to match against values.
|
54
70
|
|
55
71
|
{include:file:doc/pattern_matching.out.rb}
|
56
72
|
|
@@ -60,29 +76,24 @@ Algebraic matchers are helper objects to match algebraic values and others with
|
|
60
76
|
|
61
77
|
## What is it good for?
|
62
78
|
|
63
|
-
### Defining data
|
64
|
-
|
65
|
-
{include:file:doc/data.out.rb}
|
66
|
-
|
67
|
-
### Serialization
|
79
|
+
### Defining data structures
|
68
80
|
|
69
|
-
|
70
|
-
messege-based cross-process comunication.
|
81
|
+
<!-- {include:file:doc/data.out.rb} -->
|
71
82
|
|
72
|
-
|
83
|
+
- Simple data structures like trees
|
84
|
+
- Whenever you find yourself to pass around too many fragile Hash-Array structures
|
73
85
|
|
74
|
-
|
86
|
+
_Examples are coming shortly._
|
75
87
|
|
76
|
-
|
88
|
+
### Serialization
|
77
89
|
|
78
|
-
|
90
|
+
Algebraic types also play nice with JSON serialization and de-serialization making it ideal for defining message-based cross-process communication.
|
79
91
|
|
80
|
-
|
81
|
-
on one place, no need to track methods in two separate classes `User` and `NullUser`.
|
92
|
+
{include:file:doc/json.out.rb}
|
82
93
|
|
83
94
|
### Message matching in Actor pattern
|
84
95
|
|
85
|
-
Just small snippet
|
96
|
+
Just a small snippet how it can be used in Actor model world.
|
86
97
|
|
87
98
|
class Worker < AbstractActor
|
88
99
|
def initialize(executor)
|
@@ -97,3 +108,14 @@ Just small snippet from a gem I am still working on.
|
|
97
108
|
end
|
98
109
|
end
|
99
110
|
end
|
111
|
+
|
112
|
+
<!--
|
113
|
+
### Null Object Pattern
|
114
|
+
|
115
|
+
see {http://en.wikipedia.org/wiki/Null_Object_pattern#Ruby}.
|
116
|
+
|
117
|
+
{include:file:doc/null.out.rb}
|
118
|
+
|
119
|
+
This has advantage over a classical approach that the methods are defined
|
120
|
+
on one place, no need to track methods in two separate classes `User` and `NullUser`.
|
121
|
+
-->
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/algebrick.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2013 Petr Chalupa <git@pitr.ch>
|
1
|
+
# Copyright 2013 Petr Chalupa <git+algebrick@pitr.ch>
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -59,6 +59,7 @@ module Algebrick
|
|
59
59
|
|
60
60
|
module TypeCheck
|
61
61
|
# FIND: type checking of collections?
|
62
|
+
|
62
63
|
def Type?(value, *types)
|
63
64
|
types.any? { |t| value.is_a? t }
|
64
65
|
end
|
@@ -94,31 +95,7 @@ module Algebrick
|
|
94
95
|
|
95
96
|
def self.error(value, message, types)
|
96
97
|
raise TypeError,
|
97
|
-
"
|
98
|
-
end
|
99
|
-
|
100
|
-
public
|
101
|
-
|
102
|
-
raise 'remove deprecations' if Algebrick.version >= Gem::Version.new('0.4')
|
103
|
-
|
104
|
-
def is_kind_of?(value, *types)
|
105
|
-
warn "is_kind_of? is deprecated use Type?\n#{caller[0]}"
|
106
|
-
Type? value, *types
|
107
|
-
end
|
108
|
-
|
109
|
-
def is_kind_of!(value, *types)
|
110
|
-
warn "Type! is deprecated use Type!\n#{caller[0]}"
|
111
|
-
Type! value, *types
|
112
|
-
end
|
113
|
-
|
114
|
-
def is_matching?(value, *types)
|
115
|
-
warn "is_matching? is deprecated use Match?\n#{caller[0]}"
|
116
|
-
Match? value, *types
|
117
|
-
end
|
118
|
-
|
119
|
-
def is_matching!(value, *types)
|
120
|
-
warn "is_matching! is deprecated use Match!\n#{caller[0]}"
|
121
|
-
Match! value, *types
|
98
|
+
"Value (#{value.class}) '#{value}' #{message} any of: #{types.join('; ')}."
|
122
99
|
end
|
123
100
|
end
|
124
101
|
|
@@ -128,8 +105,6 @@ module Algebrick
|
|
128
105
|
Matchers::Any.new
|
129
106
|
end
|
130
107
|
|
131
|
-
alias_method :_, :any
|
132
|
-
|
133
108
|
def match(value, *cases)
|
134
109
|
cases = if cases.size == 1 && cases.first.is_a?(Hash)
|
135
110
|
cases.first
|
@@ -153,7 +128,7 @@ module Algebrick
|
|
153
128
|
[matcher, value || block]
|
154
129
|
end
|
155
130
|
|
156
|
-
#
|
131
|
+
# FIND: #match! raise when match is not complete on a given type
|
157
132
|
|
158
133
|
private
|
159
134
|
|
@@ -186,10 +161,6 @@ module Algebrick
|
|
186
161
|
to_m | other
|
187
162
|
end
|
188
163
|
|
189
|
-
def ^(other)
|
190
|
-
to_m ^ other
|
191
|
-
end
|
192
|
-
|
193
164
|
def !
|
194
165
|
!to_m
|
195
166
|
end
|
@@ -397,6 +368,14 @@ module Algebrick
|
|
397
368
|
self.class.type
|
398
369
|
end
|
399
370
|
|
371
|
+
def self.name
|
372
|
+
@type.to_s
|
373
|
+
end
|
374
|
+
|
375
|
+
def self.to_s
|
376
|
+
name
|
377
|
+
end
|
378
|
+
|
400
379
|
def self.type=(type)
|
401
380
|
raise if @type
|
402
381
|
@type = type
|
@@ -511,7 +490,7 @@ module Algebrick
|
|
511
490
|
end
|
512
491
|
|
513
492
|
def call(*field_matchers)
|
514
|
-
raise TypeError unless @fields
|
493
|
+
raise TypeError, "#{self} does not have any fields" unless @fields
|
515
494
|
Matchers::Product.new self, *field_matchers
|
516
495
|
end
|
517
496
|
|
@@ -863,10 +842,6 @@ module Algebrick
|
|
863
842
|
Not.new self
|
864
843
|
end
|
865
844
|
|
866
|
-
def ^(matcher)
|
867
|
-
Xor.new self, matcher
|
868
|
-
end
|
869
|
-
|
870
845
|
def assign?
|
871
846
|
@assign
|
872
847
|
end
|
@@ -981,12 +956,6 @@ module Algebrick
|
|
981
956
|
def matching?(other)
|
982
957
|
matchers.any? { |m| m === other }
|
983
958
|
end
|
984
|
-
end
|
985
|
-
|
986
|
-
class Xor < Or
|
987
|
-
def to_s
|
988
|
-
matchers.join ' ^ '
|
989
|
-
end
|
990
959
|
|
991
960
|
alias_method :super_children, :children
|
992
961
|
private :super_children
|
@@ -1158,15 +1127,6 @@ module Algebrick
|
|
1158
1127
|
field_matchers.key?(field) ? field_matchers[field] : Algebrick.any
|
1159
1128
|
end
|
1160
1129
|
|
1161
|
-
# AProduct.(:field_name)
|
1162
|
-
when field_matchers.all? { |v| v.is_a? Symbol }
|
1163
|
-
unless (dif = field_matchers - algebraic_type.field_names).empty?
|
1164
|
-
raise ArgumentError, "no #{dif} fields in #{algebraic_type}"
|
1165
|
-
end
|
1166
|
-
algebraic_type.field_names.map do |field|
|
1167
|
-
field_matchers.include?(field) ? ~Algebrick.any : Algebrick.any
|
1168
|
-
end
|
1169
|
-
|
1170
1130
|
# normal
|
1171
1131
|
else
|
1172
1132
|
field_matchers
|
@@ -1181,9 +1141,11 @@ module Algebrick
|
|
1181
1141
|
end
|
1182
1142
|
|
1183
1143
|
def to_s
|
1184
|
-
assign_to_s + "#{@algebraic_type.name}.(#{@field_matchers.join(',')})"
|
1144
|
+
assign_to_s + "#{@algebraic_type.name}.(#{@field_matchers.join(', ')})"
|
1185
1145
|
end
|
1186
1146
|
|
1147
|
+
# TODO prety_print for all matchers
|
1148
|
+
|
1187
1149
|
def ==(other)
|
1188
1150
|
other.kind_of? self.class and
|
1189
1151
|
self.algebraic_type == other.algebraic_type and
|
@@ -1224,4 +1186,21 @@ module Algebrick
|
|
1224
1186
|
end
|
1225
1187
|
end
|
1226
1188
|
|
1189
|
+
module Types
|
1190
|
+
Maybe = Algebrick.type(:v) do
|
1191
|
+
variants None = atom,
|
1192
|
+
Some = type(:v) { fields :v }
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
module Maybe
|
1196
|
+
def maybe
|
1197
|
+
match self,
|
1198
|
+
None >> nil,
|
1199
|
+
Some >-> { yield value }
|
1200
|
+
end
|
1201
|
+
end
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
include Types
|
1205
|
+
|
1227
1206
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: algebrick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Petr Chalupa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|