algebrick 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -7
  3. data/README_FULL.md +53 -31
  4. data/VERSION +1 -1
  5. data/lib/algebrick.rb +33 -54
  6. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf3eb8ddaa209e039ffb7f1c70a6e72f5598b1c9
4
- data.tar.gz: ebbd5d21d561f26c066b8a11da24620d24ec1b04
3
+ metadata.gz: d3e1690d46e7e9d3c9332eed298a46d35ec628ce
4
+ data.tar.gz: 7cf4ccef5ae69a055e9ac7806b84e0a6724d7c1b
5
5
  SHA512:
6
- metadata.gz: 1aadc9051adf421099ef8e17878ae4d03fc9eb8dd91ecb430ac65fecaa999d54b26ea64844a012da0f8a43aa13bf6a2e0eabb5620ed84aef2dc6e88c82f9691e
7
- data.tar.gz: 6aee2bb9d8ae13b4f71e61bfa40baa51696d4be1f45ce8a9f0f0898b628aee2f3234c9b92a86c700098ee9d86d8d9512f0d4e68c24f582398506392a6deddddf
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 small gem providing **algebraic types** and **pattern matching** on them for Ruby.
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
- Add some methods, don't miss the **pattern matching** example.
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 >> 0,
38
- Leaf >> 1,
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) >-> left, right do
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
- Methods are defined on **all** values of type Tree
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
@@ -1,6 +1,6 @@
1
1
  # Algebrick
2
2
 
3
- Is a small gem providing **algebraic types** and **pattern matching** on them for Ruby.
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: what is it?
13
+ ## Algebraic types
14
14
 
15
- If you ever read something about Haskell that you probably know:
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
- which is an algebraic type. This snippet defines type `Tree` which has 3 possible values:
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
- Same thing can be defined with this gem:
31
+ - `Empty`
32
+ - `Leaf` with and extra value of type `Int`
33
+ - `Node` with two values of type `Tree`
26
34
 
27
- {include:file:doc/tree1.out.rb}
35
+ and function `depth` to calculate depth of a tree which is called on the last line and evaluates to `2`.
28
36
 
29
- There are 4 kinds of algebraic types:
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
- 1. **Atom** a type that has only one value e.g. `Empty`.
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
- 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.
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
- ### Extending behavior
62
+ ### Behaviour extending
47
63
 
48
64
  {include:file:doc/extending_behavior.out.rb}
49
65
 
50
66
  ### Pattern matching
51
67
 
52
- Algebraic matchers are helper objects to match algebraic values and others with
53
- `#===` method based on theirs initialization values.
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 with a given structure
64
-
65
- {include:file:doc/data.out.rb}
66
-
67
- ### Serialization
79
+ ### Defining data structures
68
80
 
69
- Algebraic types also play nice with JSON serialization and deserialization. It is ideal for defining
70
- messege-based cross-process comunication.
81
+ <!-- {include:file:doc/data.out.rb} -->
71
82
 
72
- {include:file:doc/json.out.rb}
83
+ - Simple data structures like trees
84
+ - Whenever you find yourself to pass around too many fragile Hash-Array structures
73
85
 
74
- ### Null Object Pattern
86
+ _Examples are coming shortly._
75
87
 
76
- see {http://en.wikipedia.org/wiki/Null_Object_pattern#Ruby}.
88
+ ### Serialization
77
89
 
78
- {include:file:doc/null.out.rb}
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
- This has advantage over a classical approach that the methods are defined
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 from a gem I am still working on.
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.3.3
1
+ 0.4.0
@@ -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
- "value (#{value.class}) '#{value}' #{message} any of #{types.join(', ')}"
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
- # TODO #match! raise when match is not complete on a given type
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.3.3
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: 2013-12-11 00:00:00.000000000 Z
11
+ date: 2014-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest