algebrick 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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