pattern-matching 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NTczMjQ3ODhmZmM1ZTFjYWI0NDc3Mjk4ZDg0ZWNiZTI2MTEyMWUzNg==
5
- data.tar.gz: !binary |-
6
- YzQwN2I4N2VhNDhlOGJjZjM0MmZjNGI1OTFkZDE1NWNhMjA3ODU5ZQ==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- YTFiNzcwMzg0NmQ2OGI5MmI2MTA1Njg1YzE2NjIzMDJiNjg5NDk4OGZmMDUw
10
- YzlmMGI3NzJlMWNmNGViYTcwN2MxODY4MzA4OGRhNzZjOTJlOTg1ODUzZTcy
11
- NzYyZjgzZjIzZmE2ZjFjMjEzZTIxZDE2OGJlNDEzMDE0MDNkOTU=
12
- data.tar.gz: !binary |-
13
- MjIxM2ViMmYzY2UyNDhhMWViNzI0NTgxODNmODRmZGEyODY4MGY0YWM0Mjcw
14
- NWVmZjg1NDhjMjZjNDZjNzI2NTRmNjY3YzBlZjQwYjM4MmZkZmE2NzUwZjYx
15
- OWMwMTRkOGJiMzBkNDA4OTY1NmU2NmY3NmQ1M2Q3YzdmOWE2NDc=
2
+ SHA1:
3
+ metadata.gz: 16f7fd19a880e5d513cc2c48abb1ee42b580daf4
4
+ data.tar.gz: aabdb877f6c81402fcd459da73b60df396ad2577
5
+ SHA512:
6
+ metadata.gz: 660d6aad1d0f2eafa481e5d43b9ee581b4551b9845968b3f229d7ef2b9bd444e0f690791dfde39a8f0ec11014cf334386120b2a2ee7303d786bfb3942846c47f
7
+ data.tar.gz: bafa502a02e91ee80ae206700913dc9c208f93fbca85865967fe6242ee21ce8ba15f40dd814f9e2ca9b58d3f37944392d953b6e9ba59b3186a7fbd70f4bf040f
data/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- Copyright (c) Jerry D'Antonio -- released under the MIT license.
2
-
3
- http://www.opensource.org/licenses/mit-license.php
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
1
+ Copyright (c) Jerry D'Antonio -- released under the MIT license.
2
+
3
+ http://www.opensource.org/licenses/mit-license.php
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -1,562 +1,671 @@
1
- # PatternMatching [![Build Status](https://secure.travis-ci.org/jdantonio/pattern_matching.png)](http://travis-ci.org/jdantonio/pattern_matching?branch=master) [![Dependency Status](https://gemnasium.com/jdantonio/pattern_matching.png)](https://gemnasium.com/jdantonio/pattern_matching)
2
-
3
- A gem for adding Erlang-style function/method overloading through pattern matching to Ruby classes.
4
-
5
- *NOTE: This is a work in progress. Expect changes.*
6
-
7
- The project is hosted on the following sites:
8
-
9
- * [RubyGems project page](https://rubygems.org/gems/pattern-matching)
10
- * [Source code on GitHub](https://github.com/jdantonio/pattern_matching)
11
- * [Continuous integration on Travis-CI](https://travis-ci.org/jdantonio/pattern_matching)
12
- * [Dependency tracking on Gemnasium](https://gemnasium.com/jdantonio/pattern_matching)
13
- * [Follow me on Twitter](https://twitter.com/jerrydantonio)
14
-
15
- ## Introduction
16
-
17
- [Ruby](http://www.ruby-lang.org/en/) is my favorite programming by far. As much as I love
18
- Ruby I've always been a little disappointed that Ruby doesn't support function overloading.
19
- Function overloading tends to reduce branching and keep function signatures simpler.
20
- No sweat, I learned to do without. Then I started programming in [Erlang](http://www.erlang.org/)...
21
-
22
- I've really started to enjoy working in Erlang. Erlang is good at all the things Ruby is bad
23
- at and vice versa. Together, Ruby and Erlang make me happy. My favorite Erlang feature is,
24
- without question, [pattern matching](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching).
25
- Pattern matching is like function overloading cranked to 11. So one day I was musing on Twitter
26
- that I'd like to see Erlang-stype pattern matching in Ruby and one of my friends responded "Build it!"
27
- So I did. And here it is.
28
-
29
- ### Goals
30
-
31
- * Stay true to the spirit of Erlang pattern matching, if not the semantics
32
- * Keep the semantics as idiomatic Ruby as possible
33
- * Support features that make sense in Ruby
34
- * Exclude features that only make sense in Erlang
35
- * Avoid using *method_missing*
36
- * Keep it small (currently arround 100 LOC)
37
- * Be as fast as reasonably possible
38
-
39
- ### Features
40
-
41
- * Pattern matching for instance methods.
42
- * Pattern matching for object constructors.
43
- * Parameter count matching
44
- * Matching against primitive values
45
- * Matching by class/datatype
46
- * Matching against specific key/vaue pairs in hashes
47
- * Matching against the presence of keys within hashes
48
- * Implicit hash for last parameter
49
- * Variable-length parameter lists
50
- * Guard clauses
51
- * Recursive calls to other pattern matches
52
- * Recursive calls to superclass pattern matches
53
- * Recursive calls to superclass methods
54
- * Dispatching to superclass methods when no match is found
55
- * Reasonable error messages when no match is found
56
-
57
- ### To-do
58
-
59
- * Support class methods
60
- * Support module instance methods
61
- * Support module methods
62
-
63
- ## Supported Ruby versions
64
-
65
- MRI 1.9.x and above. Anything else and your mileage may vary.
66
-
67
- ## Install
68
-
69
- ```shell
70
- gem install pattern-matching
71
- ```
72
-
73
- or add the following line to Gemfile:
74
-
75
- ```ruby
76
- gem 'pattern-matching'
77
- ```
78
-
79
- and run `bundle install` from your shell.
80
-
81
- ## Usage
82
-
83
- First, familiarize yourself with Erlang [pattern matching](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching).
84
- This gem may not make much sense if you don't understand how Erlang dispatches
85
- functions.
86
-
87
- In the Ruby class file where you want to use pattern matching, require the
88
- *pattern_matching* gem:
89
-
90
- ```ruby
91
- require 'pattern_matching'
92
- ```
93
-
94
- Then include `PatternMatching` in your class:
95
-
96
- ```ruby
97
- require 'pattern_matching'
98
-
99
- class Foo
100
- include PatternMatching
101
-
102
- ...
103
-
104
- end
105
- ```
106
-
107
- You can then define functions with `defn` instead of the normal *def* statement.
108
- The syntax for `defn` is:
109
-
110
- ```ruby
111
- defn(:symbol_name_of_function, zero, or, more, parameters) { |block, arguments|
112
- # code to execute
113
- }
114
- ```
115
- You can then call your new function just like any other:
116
-
117
- ```ruby
118
- require 'pattern_matching'
119
-
120
- class Foo
121
- include PatternMatching
122
-
123
- defn(:hello) {
124
- puts "Hello, World!"
125
- }
126
- end
127
-
128
- foo = Foo.new
129
- foo.hello #=> "Hello, World!"
130
- ```
131
-
132
- Patterns to match against are included in the parameter list:
133
-
134
- ```ruby
135
- defn(:greet, :male) {
136
- puts "Hello, sir!"
137
- }
138
-
139
- defn(:greet, :female) {
140
- puts "Hello, ma'am!"
141
- }
142
-
143
- ...
144
-
145
- foo.hello(:male) #=> "Hello, sir!"
146
- foo.hello(:female) #=> "Hello, ma'am!"
147
- ```
148
-
149
- If a particular method call can not be matched a *NoMethodError* is thrown with
150
- a reasonably helpful error message:
151
-
152
- ```ruby
153
- foo.greet(:unknown) #=> NoMethodError: no method `greet` matching [:unknown] found for class Foo
154
- foo.greet #=> NoMethodError: no method `greet` matching [] found for class Foo
155
- ```
156
-
157
- Parameters that are expected to exist but that can take any value are considered
158
- *unbound* parameters. Unbound parameters are specified by the `_` underscore
159
- character or `UNBOUND`:
160
-
161
- ```ruby
162
- defn(:greet, _) do |name|
163
- "Hello, #{name}!"
164
- end
165
-
166
- defn(:greet, UNBOUND, UNBOUND) do |first, last|
167
- "Hello, #{first} #{last}!"
168
- end
169
-
170
- ...
171
-
172
- foo.greet('Jerry') #=> "Hello, Jerry!"
173
- ```
174
-
175
- All unbound parameters will be passed to the block in the order they are specified in the definition:
176
-
177
- ```ruby
178
- defn(:greet, _, _) do |first, last|
179
- "Hello, #{first} #{last}!"
180
- end
181
-
182
- ...
183
-
184
- foo.greet('Jerry', "D'Antonio") #=> "Hello, Jerry D'Antonio!"
185
- ```
186
-
187
- If for some reason you don't care about one or more unbound parameters within
188
- the block you can use the `_` underscore character in the block parameters list
189
- as well:
190
-
191
- ```ruby
192
- defn(:greet, _, _, _) do |first, _, last|
193
- "Hello, #{first} #{last}!"
194
- end
195
-
196
- ...
197
-
198
- foo.greet('Jerry', "I'm not going to tell you my middle name!", "D'Antonio") #=> "Hello, Jerry D'Antonio!"
199
- ```
200
-
201
- Hash parameters can match against specific keys and either bound or unbound parameters. This allows for function dispatch by hash parameters without having to dig through the hash:
202
-
203
- ```ruby
204
- defn(:hashable, {foo: :bar}) { |opts|
205
- :foo_bar
206
- }
207
- defn(:hashable, {foo: _}) { |f|
208
- f
209
- }
210
-
211
- ...
212
-
213
- foo.hashable({foo: :bar}) #=> :foo_bar
214
- foo.hashable({foo: :baz}) #=> :baz
215
- ```
216
-
217
- The Ruby idiom of the final parameter being a hash is also supported:
218
-
219
- ```ruby
220
- defn(:options, _) { |opts|
221
- opts
222
- }
223
-
224
- ...
225
-
226
- foo.options(bar: :baz, one: 1, many: 2)
227
- ```
228
-
229
- As is the Ruby idiom of variable-length argument lists. The constant `ALL` as the last parameter
230
- will match one or more arguments and pass them to the block as an array:
231
-
232
- ```ruby
233
- defn(:baz, Integer, ALL) { |int, args|
234
- [int, args]
235
- }
236
- defn(:baz, ALL) { |args|
237
- args
238
- }
239
- ```
240
-
241
- Superclass polymorphism is supported as well. If an object cannot match a method
242
- signature it will defer to the parent class:
243
-
244
- ```ruby
245
- class Bar
246
- def greet
247
- return 'Hello, World!'
248
- end
249
- end
250
-
251
- class Foo < Bar
252
- include PatternMatching
253
-
254
- defn(:greet, _) do |name|
255
- "Hello, #{name}!"
256
- end
257
- end
258
-
259
- ...
260
-
261
- foo.greet('Jerry') #=> "Hello, Jerry!"
262
- foo.greet #=> "Hello, World!"
263
- ```
264
-
265
- Guard clauses in Erlang are defined with `when` clauses between the parameter list and the function body.
266
- In Ruby, guard clauses are defined by chaining a call to `when` onto the the `defn` call and passing
267
- a block. If the guard clause evaluates to true then the function will match. If the guard evaluates
268
- to false the function will not match and pattern matching will continue:
269
-
270
- Erlang:
271
-
272
- ```erlang
273
- old_enough(X) when X >= 16 -> true;
274
- old_enough(_) -> false.
275
- ```
276
-
277
- Ruby:
278
-
279
- ```ruby
280
- defn(:old_enough, _){ true }.when{|x| x >= 16 }
281
- defn(:old_enough, _){ false }
282
- ```
283
-
284
- ### Order Matters
285
-
286
- As with Erlang, the order of pattern matches is significant. Patterns will be matched
287
- *in the order declared* and the first match will be used. If a particular function call
288
- can be matched by more than one pattern, the *first matched pattern* will be used. It
289
- is the programmer's responsibility to ensure patterns are declared in the correct order.
290
-
291
- ### Blocks and Procs and Lambdas, oh my!
292
-
293
- When using this gem it is critical to remember that `defn` takes a block and
294
- that blocks in Ruby have special rules. There are [plenty](https://www.google.com/search?q=ruby+block+proc+lambda)
295
- of good tutorials on the web explaining [blocks](http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/)
296
- and [Procs](https://coderwall.com/p/_-_mha) and [lambdas](http://railsguru.org/2010/03/learn-ruby-procs-blocks-lambda/)
297
- in Ruby. Please read them. Please don't submit a bug report if you use a
298
- `return` statement within your `defn` and your code blows up with a
299
- [LocalJumpError](http://ruby-doc.org/core-2.0/LocalJumpError.html).
300
-
301
- ## Examples
302
-
303
- For more examples see the integration tests in *spec/integration_spec.rb*.
304
-
305
- ### Simple Functions
306
-
307
- This example is based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions) in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
308
-
309
- Erlang:
310
-
311
- ```erlang
312
- greet(male, Name) ->
313
- io:format("Hello, Mr. ~s!", [Name]);
314
- greet(female, Name) ->
315
- io:format("Hello, Mrs. ~s!", [Name]);
316
- greet(_, Name) ->
317
- io:format("Hello, ~s!", [Name]).
318
- ```
319
-
320
- Ruby:
321
-
322
- ```ruby
323
- require 'pattern_matching'
324
-
325
- class Foo
326
- include PatternMatching
327
-
328
- defn(:greet, _) do |name|
329
- "Hello, #{name}!"
330
- end
331
-
332
- defn(:greet, :male, _) { |name|
333
- "Hello, Mr. #{name}!"
334
- }
335
- defn(:greet, :female, _) { |name|
336
- "Hello, Ms. #{name}!"
337
- }
338
- defn(:greet, _, _) { |_, name|
339
- "Hello, #{name}!"
340
- }
341
- end
342
- ```
343
-
344
- ### Simple Functions with Overloading
345
-
346
- This example is based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions) in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
347
-
348
- Erlang:
349
-
350
- ```erlang
351
- greet(Name) ->
352
- io:format("Hello, ~s!", [Name]).
353
-
354
- greet(male, Name) ->
355
- io:format("Hello, Mr. ~s!", [Name]);
356
- greet(female, Name) ->
357
- io:format("Hello, Mrs. ~s!", [Name]);
358
- greet(_, Name) ->
359
- io:format("Hello, ~s!", [Name]).
360
- ```
361
-
362
- Ruby:
363
-
364
- ```ruby
365
- require 'pattern_matching'
366
-
367
- class Foo
368
- include PatternMatching
369
-
370
- defn(:greet, _) do |name|
371
- "Hello, #{name}!"
372
- end
373
-
374
- defn(:greet, :male, _) { |name|
375
- "Hello, Mr. #{name}!"
376
- }
377
- defn(:greet, :female, _) { |name|
378
- "Hello, Ms. #{name}!"
379
- }
380
- defn(:greet, nil, _) { |name|
381
- "Goodbye, #{name}!"
382
- }
383
- defn(:greet, _, _) { |_, name|
384
- "Hello, #{name}!"
385
- }
386
- end
387
- ```
388
-
389
- ### Constructor Overloading
390
-
391
- ```ruby
392
- require 'pattern_matching'
393
-
394
- class Foo
395
- include PatternMatching
396
-
397
- defn(:initialize) { @name = 'baz' }
398
- defn(:initialize, _) {|name| @name = name.to_s }
399
- end
400
- ```
401
-
402
- ### Matching by Class/Datatype
403
-
404
- ```ruby
405
- require 'pattern_matching'
406
-
407
- class Foo
408
- include PatternMatching
409
-
410
- defn(:concat, Integer, Integer) { |first, second|
411
- first + second
412
- }
413
- defn(:concat, Integer, String) { |first, second|
414
- "#{first} #{second}"
415
- }
416
- defn(:concat, String, String) { |first, second|
417
- first + second
418
- }
419
- defn(:concat, Integer, _) { |first, second|
420
- first + second.to_i
421
- }
422
- end
423
- ```
424
-
425
- ### Matching a Hash Parameter
426
-
427
- ```ruby
428
- require 'pattern_matching'
429
-
430
- class Foo
431
- include PatternMatching
432
-
433
- defn(:hashable, {foo: :bar}) { |opts|
434
- # matches any hash with key :foo and value :bar
435
- :foo_bar
436
- }
437
- defn(:hashable, {foo: _, bar: _}) { |f, b|
438
- # matches any hash with keys :foo and :bar
439
- # passes the values associated with those keys to the block
440
- [f, b]
441
- }
442
- defn(:hashable, {foo: _}) { |f|
443
- # matches any hash with key :foo
444
- # passes the value associated with that key to the block
445
- # must appear AFTER the prior match or it will override that one
446
- f
447
- }
448
- defn(:hashable, {}) { ||
449
- # matches an empty hash
450
- :empty
451
- }
452
- defn(:hashable, _) { |opts|
453
- # matches any hash (or any other value)
454
- opts
455
- }
456
- end
457
-
458
- ...
459
-
460
- foo.hashable({foo: :bar}) #=> :foo_bar
461
- foo.hashable({foo: :baz}) #=> :baz
462
- foo.hashable({foo: 1, bar: 2}) #=> [1, 2]
463
- foo.hashable({foo: 1, baz: 2}) #=> 1
464
- foo.hashable({bar: :baz}) #=> {bar: :baz}
465
- foo.hashable({}) #=> :empty
466
- ```
467
-
468
- ### Variable Length Argument Lists with ALL
469
-
470
- ```ruby
471
- defn(:all, :one, ALL) { |args|
472
- args
473
- }
474
- defn(:all, :one, Integer, ALL) { |int, args|
475
- [int, args]
476
- }
477
- defn(:all, 1, _, ALL) { |var, args|
478
- [var, args]
479
- }
480
- defn(:all, ALL) { | args|
481
- args
482
- }
483
-
484
- ...
485
-
486
- foo.all(:one, 'a', 'bee', :see) #=> ['a', 'bee', :see]
487
- foo.all(:one, 1, 'bee', :see) #=> [1, 'bee', :see]
488
- foo.all(1, 'a', 'bee', :see) #=> ['a', ['bee', :see]]
489
- foo.all('a', 'bee', :see) #=> ['a', 'bee', :see]
490
- foo.all() #=> NoMethodError: no method `all` matching [] found for class Foo
491
- ```
492
-
493
- ### Guard Clauses
494
-
495
- These examples are based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions) in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
496
-
497
- Erlang:
498
-
499
- ```erlang
500
- old_enough(X) when X >= 16 -> true;
501
- old_enough(_) -> false.
502
-
503
- right_age(X) when X >= 16, X =< 104 ->
504
- true;
505
- right_age(_) ->
506
- false.
507
-
508
- wrong_age(X) when X < 16; X > 104 ->
509
- true;
510
- wrong_age(_) ->
511
- false.
512
- ```
513
-
514
- ```ruby
515
- defn(:old_enough, _){ true }.when{|x| x >= 16 }
516
- defn(:old_enough, _){ false }
517
-
518
- defn(:right_age, _) {
519
- true
520
- }.when{|x| x >= 16 && x <= 104 }
521
-
522
- defn(:right_age, _) {
523
- false
524
- }
525
-
526
- defn(:wrong_age, _) {
527
- false
528
- }.when{|x| x < 16 || x > 104 }
529
-
530
- defn(:wrong_age, _) {
531
- true
532
- }
533
- ```
534
-
535
- ## Copyright
536
-
537
- *PatternMatching* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
538
- It is free software and may be redistributed under the terms specified in the LICENSE file.
539
-
540
- ## License
541
-
542
- Released under the MIT license.
543
-
544
- http://www.opensource.org/licenses/mit-license.php
545
-
546
- > Permission is hereby granted, free of charge, to any person obtaining a copy
547
- > of this software and associated documentation files (the "Software"), to deal
548
- > in the Software without restriction, including without limitation the rights
549
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
550
- > copies of the Software, and to permit persons to whom the Software is
551
- > furnished to do so, subject to the following conditions:
552
- >
553
- > The above copyright notice and this permission notice shall be included in
554
- > all copies or substantial portions of the Software.
555
- >
556
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
557
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
558
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
559
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
560
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
561
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
562
- > THE SOFTWARE.
1
+ # PatternMatching [![Build Status](https://secure.travis-ci.org/jdantonio/pattern_matching.png)](http://travis-ci.org/jdantonio/pattern_matching?branch=master) [![Dependency Status](https://gemnasium.com/jdantonio/pattern_matching.png)](https://gemnasium.com/jdantonio/pattern_matching)
2
+
3
+ A gem for adding Erlang-style function/method overloading through pattern matching to Ruby classes.
4
+
5
+ *NOTE: This is a work in progress. Expect changes.*
6
+
7
+ The project is hosted on the following sites:
8
+
9
+ * [RubyGems project page](https://rubygems.org/gems/pattern-matching)
10
+ * [Source code on GitHub](https://github.com/jdantonio/pattern_matching)
11
+ * [Continuous integration on Travis-CI](https://travis-ci.org/jdantonio/pattern_matching)
12
+ * [Dependency tracking on Gemnasium](https://gemnasium.com/jdantonio/pattern_matching)
13
+ * [Follow me on Twitter](https://twitter.com/jerrydantonio)
14
+
15
+ ## Introduction
16
+
17
+ [Ruby](http://www.ruby-lang.org/en/) is my favorite programming by far. As much as I love
18
+ Ruby I've always been a little disappointed that Ruby doesn't support function overloading.
19
+ Function overloading tends to reduce branching and keep function signatures simpler.
20
+ No sweat, I learned to do without. Then I started programming in [Erlang](http://www.erlang.org/)...
21
+
22
+ I've really started to enjoy working in Erlang. Erlang is good at all the things Ruby is bad
23
+ at and vice versa. Together, Ruby and Erlang make me happy. My favorite Erlang feature is,
24
+ without question, [pattern matching](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching).
25
+ Pattern matching is like function overloading cranked to 11. So one day I was musing on Twitter
26
+ that I'd like to see Erlang-stype pattern matching in Ruby and one of my friends responded "Build it!"
27
+ So I did. And here it is.
28
+
29
+ For fun I've also thrown in Erlang's sparsely documented [-behaviour](http://www.erlang.org/doc/design_principles/gen_server_concepts.html).
30
+
31
+ ### Goals
32
+
33
+ * Stay true to the spirit of Erlang pattern matching, if not the semantics
34
+ * Keep the semantics as idiomatic Ruby as possible
35
+ * Support features that make sense in Ruby
36
+ * Exclude features that only make sense in Erlang
37
+ * Avoid using *method_missing*
38
+ * Keep it small (currently arround 100 LOC)
39
+ * Be as fast as reasonably possible
40
+
41
+ ### Features
42
+
43
+ * Pattern matching for instance methods.
44
+ * Pattern matching for object constructors.
45
+ * Parameter count matching
46
+ * Matching against primitive values
47
+ * Matching by class/datatype
48
+ * Matching against specific key/vaue pairs in hashes
49
+ * Matching against the presence of keys within hashes
50
+ * Implicit hash for last parameter
51
+ * Variable-length parameter lists
52
+ * Guard clauses
53
+ * Recursive calls to other pattern matches
54
+ * Recursive calls to superclass pattern matches
55
+ * Recursive calls to superclass methods
56
+ * Dispatching to superclass methods when no match is found
57
+ * Reasonable error messages when no match is found
58
+
59
+ ### For good -behavior(timeoff).
60
+
61
+ One of Ruby's greatest strengths is [duck typing](http://rubylearning.com/satishtalim/duck_typing.html).
62
+ Usually this is awesome and I'm happy to not have to deal with static typing and the compiler. Usually.
63
+ The problem with duck typing is that is is impossible in Ruby to enforce an interface definition.
64
+ I would never advocate turning Ruby into the cesspool complex object creation that Java has
65
+ unfortunately become, but occasionally it would be nice to make sure a class implements a set of
66
+ required methods. Enter Erlang's [-behavior](http://metajack.im/2008/10/29/custom-behaviors-in-erlang/)
67
+ keyword. Basically, you define a `behavior_info` then drop a `behavior` call within a class.
68
+ Forget to implement a required method and Ruby will let you know. See the examples below for details.
69
+
70
+ ## Supported Ruby versions
71
+
72
+ MRI 1.9.x and above. Anything else and your mileage may vary.
73
+
74
+ ## Install
75
+
76
+ ```shell
77
+ gem install pattern-matching
78
+ ```
79
+
80
+ or add the following line to Gemfile:
81
+
82
+ ```ruby
83
+ gem 'pattern-matching'
84
+ ```
85
+
86
+ and run `bundle install` from your shell.
87
+
88
+ ## Usage
89
+
90
+ First, familiarize yourself with Erlang [pattern matching](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching).
91
+ This gem may not make much sense if you don't understand how Erlang dispatches
92
+ functions.
93
+
94
+ In the Ruby class file where you want to use pattern matching, require the
95
+ *pattern_matching* gem:
96
+
97
+ ```ruby
98
+ require 'pattern_matching'
99
+ ```
100
+
101
+ Then include `PatternMatching` in your class:
102
+
103
+ ```ruby
104
+ require 'pattern_matching'
105
+
106
+ class Foo
107
+ include PatternMatching
108
+
109
+ ...
110
+
111
+ end
112
+ ```
113
+
114
+ You can then define functions with `defn` instead of the normal *def* statement.
115
+ The syntax for `defn` is:
116
+
117
+ ```ruby
118
+ defn(:symbol_name_of_function, zero, or, more, parameters) { |block, arguments|
119
+ # code to execute
120
+ }
121
+ ```
122
+ You can then call your new function just like any other:
123
+
124
+ ```ruby
125
+ require 'pattern_matching'
126
+
127
+ class Foo
128
+ include PatternMatching
129
+
130
+ defn(:hello) {
131
+ puts "Hello, World!"
132
+ }
133
+ end
134
+
135
+ foo = Foo.new
136
+ foo.hello #=> "Hello, World!"
137
+ ```
138
+
139
+ Patterns to match against are included in the parameter list:
140
+
141
+ ```ruby
142
+ defn(:greet, :male) {
143
+ puts "Hello, sir!"
144
+ }
145
+
146
+ defn(:greet, :female) {
147
+ puts "Hello, ma'am!"
148
+ }
149
+
150
+ ...
151
+
152
+ foo.hello(:male) #=> "Hello, sir!"
153
+ foo.hello(:female) #=> "Hello, ma'am!"
154
+ ```
155
+
156
+ If a particular method call can not be matched a *NoMethodError* is thrown with
157
+ a reasonably helpful error message:
158
+
159
+ ```ruby
160
+ foo.greet(:unknown) #=> NoMethodError: no method `greet` matching [:unknown] found for class Foo
161
+ foo.greet #=> NoMethodError: no method `greet` matching [] found for class Foo
162
+ ```
163
+
164
+ Parameters that are expected to exist but that can take any value are considered
165
+ *unbound* parameters. Unbound parameters are specified by the `_` underscore
166
+ character or `UNBOUND`:
167
+
168
+ ```ruby
169
+ defn(:greet, _) do |name|
170
+ "Hello, #{name}!"
171
+ end
172
+
173
+ defn(:greet, UNBOUND, UNBOUND) do |first, last|
174
+ "Hello, #{first} #{last}!"
175
+ end
176
+
177
+ ...
178
+
179
+ foo.greet('Jerry') #=> "Hello, Jerry!"
180
+ ```
181
+
182
+ All unbound parameters will be passed to the block in the order they are specified in the definition:
183
+
184
+ ```ruby
185
+ defn(:greet, _, _) do |first, last|
186
+ "Hello, #{first} #{last}!"
187
+ end
188
+
189
+ ...
190
+
191
+ foo.greet('Jerry', "D'Antonio") #=> "Hello, Jerry D'Antonio!"
192
+ ```
193
+
194
+ If for some reason you don't care about one or more unbound parameters within
195
+ the block you can use the `_` underscore character in the block parameters list
196
+ as well:
197
+
198
+ ```ruby
199
+ defn(:greet, _, _, _) do |first, _, last|
200
+ "Hello, #{first} #{last}!"
201
+ end
202
+
203
+ ...
204
+
205
+ foo.greet('Jerry', "I'm not going to tell you my middle name!", "D'Antonio") #=> "Hello, Jerry D'Antonio!"
206
+ ```
207
+
208
+ Hash parameters can match against specific keys and either bound or unbound parameters. This allows for
209
+ function dispatch by hash parameters without having to dig through the hash:
210
+
211
+ ```ruby
212
+ defn(:hashable, {foo: :bar}) { |opts|
213
+ :foo_bar
214
+ }
215
+ defn(:hashable, {foo: _}) { |f|
216
+ f
217
+ }
218
+
219
+ ...
220
+
221
+ foo.hashable({foo: :bar}) #=> :foo_bar
222
+ foo.hashable({foo: :baz}) #=> :baz
223
+ ```
224
+
225
+ The Ruby idiom of the final parameter being a hash is also supported:
226
+
227
+ ```ruby
228
+ defn(:options, _) { |opts|
229
+ opts
230
+ }
231
+
232
+ ...
233
+
234
+ foo.options(bar: :baz, one: 1, many: 2)
235
+ ```
236
+
237
+ As is the Ruby idiom of variable-length argument lists. The constant `ALL` as the last parameter
238
+ will match one or more arguments and pass them to the block as an array:
239
+
240
+ ```ruby
241
+ defn(:baz, Integer, ALL) { |int, args|
242
+ [int, args]
243
+ }
244
+ defn(:baz, ALL) { |args|
245
+ args
246
+ }
247
+ ```
248
+
249
+ Superclass polymorphism is supported as well. If an object cannot match a method
250
+ signature it will defer to the parent class:
251
+
252
+ ```ruby
253
+ class Bar
254
+ def greet
255
+ return 'Hello, World!'
256
+ end
257
+ end
258
+
259
+ class Foo < Bar
260
+ include PatternMatching
261
+
262
+ defn(:greet, _) do |name|
263
+ "Hello, #{name}!"
264
+ end
265
+ end
266
+
267
+ ...
268
+
269
+ foo.greet('Jerry') #=> "Hello, Jerry!"
270
+ foo.greet #=> "Hello, World!"
271
+ ```
272
+
273
+ Guard clauses in Erlang are defined with `when` clauses between the parameter list and the function body.
274
+ In Ruby, guard clauses are defined by chaining a call to `when` onto the the `defn` call and passing
275
+ a block. If the guard clause evaluates to true then the function will match. If the guard evaluates
276
+ to false the function will not match and pattern matching will continue:
277
+
278
+ Erlang:
279
+
280
+ ```erlang
281
+ old_enough(X) when X >= 16 -> true;
282
+ old_enough(_) -> false.
283
+ ```
284
+
285
+ Ruby:
286
+
287
+ ```ruby
288
+ defn(:old_enough, _){ true }.when{|x| x >= 16 }
289
+ defn(:old_enough, _){ false }
290
+ ```
291
+
292
+ ### Order Matters
293
+
294
+ As with Erlang, the order of pattern matches is significant. Patterns will be matched
295
+ *in the order declared* and the first match will be used. If a particular function call
296
+ can be matched by more than one pattern, the *first matched pattern* will be used. It
297
+ is the programmer's responsibility to ensure patterns are declared in the correct order.
298
+
299
+ ### Blocks and Procs and Lambdas, oh my!
300
+
301
+ When using this gem it is critical to remember that `defn` takes a block and
302
+ that blocks in Ruby have special rules. There are [plenty](https://www.google.com/search?q=ruby+block+proc+lambda)
303
+ of good tutorials on the web explaining [blocks](http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/)
304
+ and [Procs](https://coderwall.com/p/_-_mha) and [lambdas](http://railsguru.org/2010/03/learn-ruby-procs-blocks-lambda/)
305
+ in Ruby. Please read them. Please don't submit a bug report if you use a
306
+ `return` statement within your `defn` and your code blows up with a
307
+ [LocalJumpError](http://ruby-doc.org/core-2.0/LocalJumpError.html).
308
+
309
+ ## Examples
310
+
311
+ For more examples see the integration tests in *spec/integration_spec.rb*.
312
+
313
+ ### Simple Functions
314
+
315
+ This example is based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions) in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
316
+
317
+ Erlang:
318
+
319
+ ```erlang
320
+ greet(male, Name) ->
321
+ io:format("Hello, Mr. ~s!", [Name]);
322
+ greet(female, Name) ->
323
+ io:format("Hello, Mrs. ~s!", [Name]);
324
+ greet(_, Name) ->
325
+ io:format("Hello, ~s!", [Name]).
326
+ ```
327
+
328
+ Ruby:
329
+
330
+ ```ruby
331
+ require 'pattern_matching'
332
+
333
+ class Foo
334
+ include PatternMatching
335
+
336
+ defn(:greet, _) do |name|
337
+ "Hello, #{name}!"
338
+ end
339
+
340
+ defn(:greet, :male, _) { |name|
341
+ "Hello, Mr. #{name}!"
342
+ }
343
+ defn(:greet, :female, _) { |name|
344
+ "Hello, Ms. #{name}!"
345
+ }
346
+ defn(:greet, _, _) { |_, name|
347
+ "Hello, #{name}!"
348
+ }
349
+ end
350
+ ```
351
+
352
+ ### Simple Functions with Overloading
353
+
354
+ This example is based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions) in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
355
+
356
+ Erlang:
357
+
358
+ ```erlang
359
+ greet(Name) ->
360
+ io:format("Hello, ~s!", [Name]).
361
+
362
+ greet(male, Name) ->
363
+ io:format("Hello, Mr. ~s!", [Name]);
364
+ greet(female, Name) ->
365
+ io:format("Hello, Mrs. ~s!", [Name]);
366
+ greet(_, Name) ->
367
+ io:format("Hello, ~s!", [Name]).
368
+ ```
369
+
370
+ Ruby:
371
+
372
+ ```ruby
373
+ require 'pattern_matching'
374
+
375
+ class Foo
376
+ include PatternMatching
377
+
378
+ defn(:greet, _) do |name|
379
+ "Hello, #{name}!"
380
+ end
381
+
382
+ defn(:greet, :male, _) { |name|
383
+ "Hello, Mr. #{name}!"
384
+ }
385
+ defn(:greet, :female, _) { |name|
386
+ "Hello, Ms. #{name}!"
387
+ }
388
+ defn(:greet, nil, _) { |name|
389
+ "Goodbye, #{name}!"
390
+ }
391
+ defn(:greet, _, _) { |_, name|
392
+ "Hello, #{name}!"
393
+ }
394
+ end
395
+ ```
396
+
397
+ ### Constructor Overloading
398
+
399
+ ```ruby
400
+ require 'pattern_matching'
401
+
402
+ class Foo
403
+ include PatternMatching
404
+
405
+ defn(:initialize) { @name = 'baz' }
406
+ defn(:initialize, _) {|name| @name = name.to_s }
407
+ end
408
+ ```
409
+
410
+ ### Matching by Class/Datatype
411
+
412
+ ```ruby
413
+ require 'pattern_matching'
414
+
415
+ class Foo
416
+ include PatternMatching
417
+
418
+ defn(:concat, Integer, Integer) { |first, second|
419
+ first + second
420
+ }
421
+ defn(:concat, Integer, String) { |first, second|
422
+ "#{first} #{second}"
423
+ }
424
+ defn(:concat, String, String) { |first, second|
425
+ first + second
426
+ }
427
+ defn(:concat, Integer, _) { |first, second|
428
+ first + second.to_i
429
+ }
430
+ end
431
+ ```
432
+
433
+ ### Matching a Hash Parameter
434
+
435
+ ```ruby
436
+ require 'pattern_matching'
437
+
438
+ class Foo
439
+ include PatternMatching
440
+
441
+ defn(:hashable, {foo: :bar}) { |opts|
442
+ # matches any hash with key :foo and value :bar
443
+ :foo_bar
444
+ }
445
+ defn(:hashable, {foo: _, bar: _}) { |f, b|
446
+ # matches any hash with keys :foo and :bar
447
+ # passes the values associated with those keys to the block
448
+ [f, b]
449
+ }
450
+ defn(:hashable, {foo: _}) { |f|
451
+ # matches any hash with key :foo
452
+ # passes the value associated with that key to the block
453
+ # must appear AFTER the prior match or it will override that one
454
+ f
455
+ }
456
+ defn(:hashable, {}) { ||
457
+ # matches an empty hash
458
+ :empty
459
+ }
460
+ defn(:hashable, _) { |opts|
461
+ # matches any hash (or any other value)
462
+ opts
463
+ }
464
+ end
465
+
466
+ ...
467
+
468
+ foo.hashable({foo: :bar}) #=> :foo_bar
469
+ foo.hashable({foo: :baz}) #=> :baz
470
+ foo.hashable({foo: 1, bar: 2}) #=> [1, 2]
471
+ foo.hashable({foo: 1, baz: 2}) #=> 1
472
+ foo.hashable({bar: :baz}) #=> {bar: :baz}
473
+ foo.hashable({}) #=> :empty
474
+ ```
475
+
476
+ ### Variable Length Argument Lists with ALL
477
+
478
+ ```ruby
479
+ defn(:all, :one, ALL) { |args|
480
+ args
481
+ }
482
+ defn(:all, :one, Integer, ALL) { |int, args|
483
+ [int, args]
484
+ }
485
+ defn(:all, 1, _, ALL) { |var, args|
486
+ [var, args]
487
+ }
488
+ defn(:all, ALL) { | args|
489
+ args
490
+ }
491
+
492
+ ...
493
+
494
+ foo.all(:one, 'a', 'bee', :see) #=> ['a', 'bee', :see]
495
+ foo.all(:one, 1, 'bee', :see) #=> [1, 'bee', :see]
496
+ foo.all(1, 'a', 'bee', :see) #=> ['a', ['bee', :see]]
497
+ foo.all('a', 'bee', :see) #=> ['a', 'bee', :see]
498
+ foo.all() #=> NoMethodError: no method `all` matching [] found for class Foo
499
+ ```
500
+
501
+ ### Guard Clauses
502
+
503
+ These examples are based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions)
504
+ in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
505
+
506
+ Erlang:
507
+
508
+ ```erlang
509
+ old_enough(X) when X >= 16 -> true;
510
+ old_enough(_) -> false.
511
+
512
+ right_age(X) when X >= 16, X =< 104 ->
513
+ true;
514
+ right_age(_) ->
515
+ false.
516
+
517
+ wrong_age(X) when X < 16; X > 104 ->
518
+ true;
519
+ wrong_age(_) ->
520
+ false.
521
+ ```
522
+
523
+ ```ruby
524
+ defn(:old_enough, _){ true }.when{|x| x >= 16 }
525
+ defn(:old_enough, _){ false }
526
+
527
+ defn(:right_age, _) {
528
+ true
529
+ }.when{|x| x >= 16 && x <= 104 }
530
+
531
+ defn(:right_age, _) {
532
+ false
533
+ }
534
+
535
+ defn(:wrong_age, _) {
536
+ false
537
+ }.when{|x| x < 16 || x > 104 }
538
+
539
+ defn(:wrong_age, _) {
540
+ true
541
+ }
542
+ ```
543
+
544
+ ### Behavior
545
+
546
+ The `behavior` functionality is not import by default. It requires a separate require statement:
547
+
548
+ ```ruby
549
+ require 'behavior'
550
+
551
+ # -or-
552
+
553
+ require 'behaviour'
554
+ ```
555
+
556
+ Next, declare a behavior using the `behavior_info` function (this function should sit outside
557
+ of any module/class definition, but will probably work regardless). The first parameter to
558
+ `behavior_info` (or `behaviour_info`) is a symbol name for the behavior. The remaining parameter
559
+ is a hash of function names and their arity:
560
+
561
+ ```ruby
562
+ behaviour_info(:gen_foo, foo: 0, bar: 1, baz: 2)
563
+
564
+ # -or (for the Java/C# crowd)
565
+
566
+ interface(:gen_foo, foo: 0, bar: 1, baz: 2)
567
+
568
+ ```
569
+
570
+ Each function name can be listed only once and the arity must follow the rules of the
571
+ [Method#arity](http://ruby-doc.org/core-1.9.3/Method.html#method-i-arity) function.
572
+ Though not explicitly documented, block arguments do not count toward a method's arity.
573
+ methods defined using this gem's `defn` function will always have an arity of -1,
574
+ regardless of how many overloads are defined.
575
+
576
+ To enforce a behavior on a class simply call the `behavior` function within the class,
577
+ passing the name of the desired behavior:
578
+
579
+ ```ruby
580
+ class Foo
581
+ behavior(:gen_foo)
582
+ ...
583
+ end
584
+
585
+ # or use the idiomatic Erlang spelling
586
+ class Bar
587
+ behaviour(:gen_foo)
588
+ ...
589
+ end
590
+
591
+ # or use the idiomatic Rails syntax
592
+ class Baz
593
+ behaves_as :gen_foo
594
+ ...
595
+ end
596
+ ```
597
+
598
+ Make sure you the implement the required methods in your class. If you don't, Ruby will
599
+ raise an exception when you try to create an object from the class:
600
+
601
+ ```ruby
602
+ Baz.new #=> ArgumentError: undefined callback functions in Baz (behavior 'gen_foo')
603
+ ```
604
+
605
+ As an added bonus, Ruby [Object](http://ruby-doc.org/core-1.9.3/Object.html) will be
606
+ monkey-patched with a `behaves_as?` predicate method.
607
+
608
+ A complete example:
609
+
610
+ ```ruby
611
+ behaviour_info(:gen_foo, foo: 0, bar: 1, baz: 2, boom: -1, bam: :any)
612
+
613
+ class Foo
614
+ behavior(:gen_foo)
615
+
616
+ def foo
617
+ return 'foo/0'
618
+ end
619
+
620
+ def bar(one, &block)
621
+ return 'bar/1'
622
+ end
623
+
624
+ def baz(one, two)
625
+ return 'baz/2'
626
+ end
627
+
628
+ def boom(*args)
629
+ return 'boom/-1'
630
+ end
631
+
632
+ def bam
633
+ return 'bam!'
634
+ end
635
+ end
636
+
637
+ foo = Foo.new
638
+
639
+ foo.behaves_as? :gen_foo #=> true
640
+ foo.behaves_as?(:bogus) #=> false
641
+ 'foo'.behaves_as? :gen_foo #=> false
642
+ ```
643
+
644
+ ## Copyright
645
+
646
+ *PatternMatching* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
647
+ It is free software and may be redistributed under the terms specified in the LICENSE file.
648
+
649
+ ## License
650
+
651
+ Released under the MIT license.
652
+
653
+ http://www.opensource.org/licenses/mit-license.php
654
+
655
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
656
+ > of this software and associated documentation files (the "Software"), to deal
657
+ > in the Software without restriction, including without limitation the rights
658
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
659
+ > copies of the Software, and to permit persons to whom the Software is
660
+ > furnished to do so, subject to the following conditions:
661
+ >
662
+ > The above copyright notice and this permission notice shall be included in
663
+ > all copies or substantial portions of the Software.
664
+ >
665
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
666
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
667
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
668
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
669
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
670
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
671
+ > THE SOFTWARE.