functional-ruby 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/README.md +14 -12
  4. data/doc/memo.md +192 -0
  5. data/doc/pattern_matching.md +481 -0
  6. data/doc/protocol.md +219 -0
  7. data/doc/record.md +247 -0
  8. data/lib/functional/abstract_struct.rb +8 -8
  9. data/lib/functional/delay.rb +31 -38
  10. data/lib/functional/either.rb +48 -45
  11. data/lib/functional/final_struct.rb +23 -34
  12. data/lib/functional/final_var.rb +20 -21
  13. data/lib/functional/memo.rb +33 -24
  14. data/lib/functional/method_signature.rb +1 -2
  15. data/lib/functional/option.rb +7 -7
  16. data/lib/functional/pattern_matching.rb +12 -10
  17. data/lib/functional/protocol.rb +2 -4
  18. data/lib/functional/protocol_info.rb +5 -3
  19. data/lib/functional/record.rb +82 -16
  20. data/lib/functional/synchronization.rb +88 -0
  21. data/lib/functional/tuple.rb +14 -4
  22. data/lib/functional/type_check.rb +0 -2
  23. data/lib/functional/union.rb +5 -4
  24. data/lib/functional/value_struct.rb +5 -3
  25. data/lib/functional/version.rb +1 -1
  26. data/spec/functional/complex_pattern_matching_spec.rb +1 -2
  27. data/spec/functional/configuration_spec.rb +0 -2
  28. data/spec/functional/delay_spec.rb +0 -2
  29. data/spec/functional/either_spec.rb +0 -1
  30. data/spec/functional/final_struct_spec.rb +0 -1
  31. data/spec/functional/final_var_spec.rb +0 -2
  32. data/spec/functional/memo_spec.rb +7 -10
  33. data/spec/functional/option_spec.rb +0 -1
  34. data/spec/functional/pattern_matching_spec.rb +0 -1
  35. data/spec/functional/protocol_info_spec.rb +0 -2
  36. data/spec/functional/protocol_spec.rb +1 -3
  37. data/spec/functional/record_spec.rb +170 -87
  38. data/spec/functional/tuple_spec.rb +0 -1
  39. data/spec/functional/type_check_spec.rb +0 -2
  40. data/spec/functional/union_spec.rb +0 -1
  41. data/spec/functional/value_struct_spec.rb +0 -1
  42. metadata +14 -29
  43. data/doc/memo.txt +0 -192
  44. data/doc/pattern_matching.txt +0 -485
  45. data/doc/protocol.txt +0 -221
  46. data/doc/record.txt +0 -207
  47. data/doc/thread_safety.txt +0 -17
@@ -1,221 +0,0 @@
1
- # @!macro [new] protocol
2
- #
3
- # ## Rationale
4
- #
5
- # Traditional object orientation implements polymorphism inheritance. The *Is-A*
6
- # relationship indicates that one object "is a" instance of another object.
7
- # Implicit in this relationship, however, is the concept of [type](http://en.wikipedia.org/wiki/Data_type).
8
- # Every Ruby object has a *type*, and that type is the name of its `Class` or
9
- # `Module`. The Ruby runtime provides a number of reflective methods that allow
10
- # objects to be interrogated for type information. The principal of thses is the
11
- # `is_a?` (alias `kind_of`) method defined in class `Object`.
12
- #
13
- # Unlike many traditional object oriented languages, Ruby is a [dynamically typed](http://en.wikipedia.org/wiki/Dynamic_typing#DYNAMIC)
14
- # language. Types exist but the runtime is free to cast one type into another
15
- # at any time. Moreover, Ruby is a [duck typed](http://en.wikipedia.org/wiki/Duck_typing).
16
- # If an object "walks like a duck and quacks like a duck then it must be a duck."
17
- # When a method needs called on an object Ruby does not check the type of the object,
18
- # it simply checks to see if the requested function exists with the proper
19
- # [arity](http://en.wikipedia.org/wiki/Arity) and, if it does, dispatches the call.
20
- # The duck type analogue to `is_a?` is `respond_to?`. Thus an object can be interrogated
21
- # for its behavior rather than its type.
22
- #
23
- # Although Ruby offers several methods for reflecting on the behavior of a module/class/object,
24
- # such as `method`, `instance_methods`, `const_defined?`, the aforementioned `respond_to?`,
25
- # and others, Ruby lacks a convenient way to group collections of methods in any way that
26
- # does not involve type. Both modules and classes provide mechanisms for combining
27
- # methods into cohesive abstractions, but they both imply type. This is anathema to Ruby's
28
- # dynamism and duck typing. What Ruby needs is a way to collect a group of method names
29
- # and signatures into a cohesive collection that embraces duck typing and dynamic dispatch.
30
- # This is what protocols do.
31
- #
32
- # ## Specifying
33
- #
34
- # A "protocol" is a loose collection of method, attribute, and constant names with optional
35
- # arity values. The protocol definition does very little on its own. The power of protocols
36
- # is that they provide a way for modules, classes, and objects to be interrogated with
37
- # respect to common behavior, not common type. At the core a protocol is nothing more
38
- # than a collection of `respond_to?` method calls that ask the question "Does this thing
39
- # *behave* like this other thing."
40
- #
41
- # Protocols are specified with the `Functional::SpecifyProtocol` method. It takes one parameter,
42
- # the name of the protocol, and a block which contains the protocol specification. This registers
43
- # the protocol specification and makes it available for use later when interrogating ojects
44
- # for their behavior.
45
- #
46
- # ### Defining Attributes, Methods, and Constants
47
- #
48
- # A single protocol specification can include definition for attributes, methods,
49
- # and constants. Methods and attributes can be defined as class/module methods or
50
- # as instance methods. Within the a protocol specification each item must include
51
- # the symbolic name of the item being defined.
52
- #
53
- # ```ruby
54
- # Functional::SpecifyProtocol(:KitchenSink) do
55
- # instance_method :instance_method
56
- # class_method :class_method
57
- # attr_accessor :attr_accessor
58
- # attr_reader :attr_reader
59
- # attr_writer :attr_writer
60
- # class_attr_accessor :class_attr_accessor
61
- # class_attr_reader :class_attr_reader
62
- # class_attr_writer :class_attr_writer
63
- # constant :CONSTANT
64
- # end
65
- # ```
66
- #
67
- # Definitions for accessors are expanded at specification into the apprporiate
68
- # method(s). Which means that this:
69
- #
70
- # ```ruby
71
- # Functional::SpecifyProtocol(:Name) do
72
- # attr_accessor :first
73
- # attr_accessor :middle
74
- # attr_accessor :last
75
- # attr_accessor :suffix
76
- # end
77
- # ```
78
- #
79
- # is the same as:
80
- #
81
- # ```ruby
82
- # Functional::SpecifyProtocol(:Name) do
83
- # instance_method :first
84
- # instance_method :first=
85
- # instance_method :middle
86
- # instance_method :middle=
87
- # instance_method :last
88
- # instance_method :last=
89
- # instance_method :suffix
90
- # instance_method :suffix=
91
- # end
92
- # ```
93
- #
94
- # Protocols only care about the methods themselves, not how they were declared.
95
- #
96
- # ### Arity
97
- #
98
- # In addition to defining *which* methods exist, the required method arity can
99
- # indicated. Arity is optional. When no arity is given any arity will be expected.
100
- # The arity rules follow those defined for the `#arity` method of Ruby's
101
- # [Method class](http://www.ruby-doc.org/core-2.1.2/Method.html#method-i-arity):
102
- #
103
- # * Methods with a fixed number of arguments have a non-negative arity
104
- # * Methods with optional arguments have an arity `-n - 1`, where n is the number of required arguments
105
- # * Methods with a variable number of arguments have an arity of `-1`
106
- #
107
- # ```ruby
108
- # Functional::SpecifyProtocol(:Foo) do
109
- # instance_method :any_args
110
- # instance_method :no_args, 0
111
- # instance_method :three_args, 3
112
- # instance_method :optional_args, -2
113
- # instance_method :variable_args, -1
114
- # end
115
- #
116
- # class Bar
117
- #
118
- # def any_args(a, b, c=1, d=2, *args)
119
- # end
120
- #
121
- # def no_args
122
- # end
123
- #
124
- # def three_args(a, b, c)
125
- # end
126
- #
127
- # def optional_args(a, b=1, c=2)
128
- # end
129
- #
130
- # def variable_args(*args)
131
- # end
132
- # end
133
- # ```
134
- #
135
- # ## Reflection
136
- #
137
- # Once a protocol has been defined, any class, method, or object may be interrogated
138
- # for adherence to one or more protocol specifications. The methods of the
139
- # `Functional::Protocol` classes provide this capability. The `Satisfy?` method
140
- # takes a module/class/object as the first parameter and one or more protocol names
141
- # as the second and subsequent parameters. It returns a boolean value indicating
142
- # whether the given object satisfies the protocol requirements:
143
- #
144
- # ```ruby
145
- # Functional::SpecifyProtocol(:Queue) do
146
- # instance_method :push, 1
147
- # instance_method :pop, 0
148
- # instance_method :length, 0
149
- # end
150
- #
151
- # Functional::SpecifyProtocol(:List) do
152
- # instance_method :[]=, 2
153
- # instance_method :[], 1
154
- # instance_method :each, 0
155
- # instance_method :length, 0
156
- # end
157
- #
158
- # Functional::Protocol::Satisfy?(Queue, :Queue) #=> true
159
- # Functional::Protocol::Satisfy?(Queue, :List) #=> false
160
- #
161
- # list = [1, 2, 3]
162
- # Functional::Protocol::Satisfy?(Array, :List, :Queue) #=> true
163
- # Functional::Protocol::Satisfy?(list, :List, :Queue) #=> true
164
- #
165
- # Functional::Protocol::Satisfy?(Hash, :Queue) #=> false
166
- #
167
- # Functional::Protocol::Satisfy?('foo bar baz', :List) #=> false
168
- # ```
169
- #
170
- # The `Satisfy!` method performs the exact same check but instead raises an exception
171
- # when the protocol is not satisfied:
172
- #
173
- # ```
174
- # 2.1.2 :021 > Functional::Protocol::Satisfy!(Queue, :List)
175
- # Functional::ProtocolError: Value (Class) 'Thread::Queue' does not behave as all of: :List.
176
- # from /Projects/functional-ruby/lib/functional/protocol.rb:67:in `error'
177
- # from /Projects/functional-ruby/lib/functional/protocol.rb:36:in `Satisfy!'
178
- # from (irb):21
179
- # ...
180
- # ```
181
- # The `Functional::Protocol` module can be included within other classes
182
- # to eliminate the namespace requirement when calling:
183
- #
184
- # ```ruby
185
- # class MessageFormatter
186
- # include Functional::Protocol
187
- #
188
- # def format(message)
189
- # if Satisfy?(message, :Internal)
190
- # format_internal_message(message)
191
- # elsif Satisfy?(message, :Error)
192
- # format_error_message(message)
193
- # else
194
- # format_generic_message(message)
195
- # end
196
- # end
197
- #
198
- # private
199
- #
200
- # def format_internal_message(message)
201
- # # format the message...
202
- # end
203
- #
204
- # def format_error_message(message)
205
- # # format the message...
206
- # end
207
- #
208
- # def format_generic_message(message)
209
- # # format the message...
210
- # end
211
- # ```
212
- #
213
- # ## Inspiration
214
- #
215
- # Protocols and similar functionality exist in several other programming languages.
216
- # A few languages that provided inspiration for this inplementation are:
217
- #
218
- # * Clojure [protocol](http://clojure.org/protocols)
219
- # * Erlang [behaviours](http://www.erlang.org/doc/design_principles/des_princ.html#id60128)
220
- # * Objective-C [protocol](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithProtocols/WorkingwithProtocols.html)
221
- # (and the corresponding Swift [protocol](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html))
@@ -1,207 +0,0 @@
1
- # @!macro [new] record
2
- #
3
- # ## Declaration
4
- #
5
- # A `Record` class is declared in a manner identical to that used with Ruby's `Struct`.
6
- # The class method `new` is called with a list of one or more field names (symbols).
7
- # A new class will then be dynamically generated along with the necessary reader
8
- # attributes, one for each field. The newly created class will be anonymous and
9
- # will mixin `Functional::AbstractStruct`. The best practice is to assign the newly
10
- # created record class to a constant:
11
- #
12
- # ```ruby
13
- # Customer = Functional::Record.new(:name, :address) #=> Customer
14
- # ```
15
- #
16
- # Alternatively, the name of the record class, as a string, can be given as the
17
- # first parameter. In this case the new record class will be created as a constant
18
- # within the `Record` module:
19
- #
20
- # ```ruby
21
- # Functional::Record.new("Customer", :name, :address) #=> Functional::Record::Customer
22
- # ```
23
- #
24
- # **NOTE:** The `new` method of `Record` does not accept a block the way Ruby's `Struct`
25
- # does. The block passed to the `new` method of `Record` is used to set mandatory fields
26
- # and default values (see below). It is *not* used for additional class declarations.
27
- #
28
- # ### Construction
29
- #
30
- # Construction of a new object from a record is slightly different than for a Ruby `Struct`.
31
- # The constructor for a struct class may take zero or more field values and will use those
32
- # values to popuate the fields. The values passed to the constructor are assumed to be in
33
- # the same order as the fields were defined. This works for a struct because it is
34
- # mutable--the field values may be changed after instanciation. Therefore it is not
35
- # necessary to provide all values to a stuct at creation. This is not the case for a
36
- # record. A record is immutable. The values for all its fields must be set at instanciation
37
- # because they cannot be changed later. When creating a new record object the constructor
38
- # will accept a collection of field/value pairs in hash syntax and will create the new
39
- # record with the given values:
40
- #
41
- # ```ruby
42
- # Customer.new(name: 'Dave', address: '123 Main')
43
- # #=> #<record Customer :name=>"Dave", :address=>"123 Main">
44
- #
45
- # Functional::Record::Customer.new(name: 'Dave', address: '123 Main')
46
- # #=> #<record Functional::Record::Customer :name=>"Dave", :address=>"123 Main">
47
- # ```
48
- #
49
- # ### Default Values
50
- #
51
- # By default, all record fields are set to `nil` at instanciation unless explicity set
52
- # via the constructor. It is possible to specify default values other than `nil` for
53
- # zero or more of the fields when a new record class is created. The `new` method of
54
- # `Record` accepts a block which can be used to declare new default values:
55
- #
56
- # ```ruby
57
- # Address = Functional::Record.new(:street_line_1, :street_line_2,
58
- # :city, :state, :postal_code, :country) do
59
- # default :state, 'Ohio'
60
- # default :country, 'USA'
61
- # end
62
- # #=> Address
63
- # ```
64
- #
65
- # When a new object is created from a record class with explicit default values, those
66
- # values will be used for the appropriate fields when no other value is given at
67
- # construction:
68
- #
69
- # ```ruby
70
- # Address.new(street_line_1: '2401 Ontario St',
71
- # city: 'Cleveland', postal_code: 44115)
72
- # #=> #<record Address :street_line_1=>"2401 Ontario St", :street_line_2=>nil, :city=>"Cleveland", :state=>"Ohio", :postal_code=>44115, :country=>"USA">
73
- # ```
74
- #
75
- # Of course, if a value for a field is given at construction that value will be used instead
76
- # of the custom default:
77
- #
78
- # ```ruby
79
- # Address.new(street_line_1: '1060 W Addison St',
80
- # city: 'Chicago', state: 'Illinois', postal_code: 60613)
81
- # #=> #<record Address :street_line_1=>"1060 W Addison St", :street_line_2=>nil, :city=>"Chicago", :state=>"Illinois", :postal_code=>60613, :country=>"USA">
82
- # ```
83
- #
84
- # ### Mandatory Fields
85
- #
86
- # By default, all record fields are optional. It is perfectly legal for a record
87
- # object to exist with all its fields set to `nil`. During declaration of a new record
88
- # class the block passed to `Record.new` can also be used to indicate which fields
89
- # are mandatory. When a new object is created from a record with mandatory fields
90
- # an exception will be thrown if any of those fields are nil:
91
- #
92
- # ```ruby
93
- # Name = Functional::Record.new(:first, :middle, :last, :suffix) do
94
- # mandatory :first, :last
95
- # end
96
- # #=> Name
97
- #
98
- # Name.new(first: 'Joe', last: 'Armstrong')
99
- # #=> #<record Name :first=>"Joe", :middle=>nil, :last=>"Armstrong", :suffix=>nil>
100
- #
101
- # Name.new(first: 'Matz') #=> ArgumentError: mandatory fields must not be nil
102
- # ```
103
- #
104
- # Of course, declarations for default values and mandatory fields may be used
105
- # together:
106
- #
107
- # ```ruby
108
- # Person = Functional::Record.new(:first_name, :middle_name, :last_name,
109
- # :street_line_1, :street_line_2,
110
- # :city, :state, :postal_code, :country) do
111
- # mandatory :first_name, :last_name
112
- # mandatory :country
113
- # default :state, 'Ohio'
114
- # default :country, 'USA'
115
- # end
116
- # #=> Person
117
- # ```
118
- #
119
- # ### Default Value Memoization
120
- #
121
- # Note that the block provided to `Record.new` is processed once and only once
122
- # when the new record class is declared. Thereafter the results are memoized
123
- # and copied (via `clone`, unless uncloneable) each time a new record object
124
- # is created. Default values should be simple types like `String`, `Fixnum`,
125
- # and `Boolean`. If complex operations need performed when setting default
126
- # values the a `Class` should be used instead of a `Record`.
127
- #
128
- # ### Why Declaration Differs from Ruby's Struct
129
- #
130
- # Those familiar with Ruby's `Struct` class will notice one important
131
- # difference when declaring a `Record`: the block passes to `new` cannot be
132
- # used to define additional methods. When declaring a new class created from a
133
- # Ruby `Struct` the block can perform any additional class definition that
134
- # could be done had the class be defined normally. The excellent
135
- # [Values](https://github.com/tcrayford/Values) supports this same behavior.
136
- # `Record` does not allow additional class definitions during declaration for
137
- # one simple reason: doing so violates two very important tenets of functional
138
- # programming. Specifically, immutability and the separation of data from
139
- # operations.
140
- #
141
- # `Record` exists for the purpose of creating immutable objects. If additional
142
- # instance methods were to be defined on a record class it would be possible
143
- # to violate immutability. Not only could additional, mutable state be added
144
- # to the class, but the existing immutable attributes could be overridden by
145
- # mutable methods. The security of providing an immutable object would be
146
- # completely shattered, thus defeating the original purpose of the record
147
- # class. Of course it would be possible to allow this feature and trust the
148
- # programmer to not violate the intended immutability of class, but opening
149
- # `Record` to the *possibility* of immutability violation is unnecessary and
150
- # unwise.
151
- #
152
- # More important than the potential for immutability violations is the fact
153
- # the adding additional methods to a record violates the principal of
154
- # separating data from operations on that data. This is one of the core ideas
155
- # in functional programming. Data is defined in pure structures that contain
156
- # no behavior and operations on that data are provided by polymorphic
157
- # functions. This may seem counterintuitive to object oriented programmers,
158
- # but that is the nature of functional programming. Adding behavior to a
159
- # record, even when that behavior does not violate immutability, is still
160
- # anathema to functional programming, and it is why records in languages like
161
- # Erlang and Clojure do not have functions defined within them.
162
- #
163
- # Should additional methods need defined on a `Record` class, the appropriate
164
- # practice is to declare the record class then declare another class which
165
- # extends the record. The record class remains pure data and the subclass
166
- # contains additional operations on that data.
167
- #
168
- # ```ruby
169
- # NameRecord = Functional::Record.new(:first, :middle, :last, :suffix) do
170
- # mandatory :first, :last
171
- # end
172
- #
173
- # class Name < NameRecord
174
- # def full_name
175
- # "#{first} #{last}"
176
- # end
177
- #
178
- # def formal_name
179
- # name = [first, middle, last].select{|s| ! s.to_s.empty?}.join(' ')
180
- # suffix.to_s.empty? ? name : name + ", #{suffix}"
181
- # end
182
- # end
183
- #
184
- # jerry = Name.new(first: 'Jerry', last: "D'Antonio")
185
- # ted = Name.new(first: 'Ted', middle: 'Theodore', last: 'Logan', suffix: 'Esq.')
186
- #
187
- # jerry.formal_name #=> "Jerry D'Antonio"
188
- # ted.formal_name #=> "Ted Theodore Logan, Esq."
189
- # ```
190
- #
191
- # ## Inspiration
192
- #
193
- # Neither struct nor records are new to computing. Both have been around for a very
194
- # long time. Mutable structs can be found in many languages including
195
- # [Ruby](http://www.ruby-doc.org/core-2.1.2/Struct.html),
196
- # [Go](http://golang.org/ref/spec#Struct_types),
197
- # [C](http://en.wikipedia.org/wiki/Struct_(C_programming_language)),
198
- # and [C#](http://msdn.microsoft.com/en-us/library/ah19swz4.aspx),
199
- # just to name a few. Immutable records exist primarily in functional languages
200
- # like [Haskell](http://en.wikibooks.org/wiki/Haskell/More_on_datatypes#Named_Fields_.28Record_Syntax.29),
201
- # Clojure, and Erlang. The latter two are the main influences for this implementation.
202
- #
203
- # * [Ruby Struct](http://www.ruby-doc.org/core-2.1.2/Struct.html)
204
- # * [Clojure Datatypes](http://clojure.org/datatypes)
205
- # * [Clojure *defrecord* macro](http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/defrecord)
206
- # * [Erlang Records (Reference)](http://www.erlang.org/doc/reference_manual/records.html)
207
- # * [Erlang Records (Examples)](http://www.erlang.org/doc/programming_examples/records.html)
@@ -1,17 +0,0 @@
1
- # @!macro [new] thread_safe_immutable_object
2
- #
3
- # @note This is an immutable, read-only, frozen, thread safe object that can
4
- # be used in concurrent systems. Thread safety guarantees *cannot* be made
5
- # about objects contained *within* this object, however. Ruby variables are
6
- # mutable references to mutable objects. This cannot be changed. The best
7
- # practice it to only encapsulate immutable, frozen, or thread safe objects.
8
- # Ultimately, thread safety is the responsibility of the programmer.
9
- #
10
- # @!macro [new] thread_safe_final_object
11
- #
12
- # @note This is a write-once, read-many, thread safe object that can
13
- # be used in concurrent systems. Thread safety guarantees *cannot* be made
14
- # about objects contained *within* this object, however. Ruby variables are
15
- # mutable references to mutable objects. This cannot be changed. The best
16
- # practice it to only encapsulate immutable, frozen, or thread safe objects.
17
- # Ultimately, thread safety is the responsibility of the programmer.