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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +14 -12
- data/doc/memo.md +192 -0
- data/doc/pattern_matching.md +481 -0
- data/doc/protocol.md +219 -0
- data/doc/record.md +247 -0
- data/lib/functional/abstract_struct.rb +8 -8
- data/lib/functional/delay.rb +31 -38
- data/lib/functional/either.rb +48 -45
- data/lib/functional/final_struct.rb +23 -34
- data/lib/functional/final_var.rb +20 -21
- data/lib/functional/memo.rb +33 -24
- data/lib/functional/method_signature.rb +1 -2
- data/lib/functional/option.rb +7 -7
- data/lib/functional/pattern_matching.rb +12 -10
- data/lib/functional/protocol.rb +2 -4
- data/lib/functional/protocol_info.rb +5 -3
- data/lib/functional/record.rb +82 -16
- data/lib/functional/synchronization.rb +88 -0
- data/lib/functional/tuple.rb +14 -4
- data/lib/functional/type_check.rb +0 -2
- data/lib/functional/union.rb +5 -4
- data/lib/functional/value_struct.rb +5 -3
- data/lib/functional/version.rb +1 -1
- data/spec/functional/complex_pattern_matching_spec.rb +1 -2
- data/spec/functional/configuration_spec.rb +0 -2
- data/spec/functional/delay_spec.rb +0 -2
- data/spec/functional/either_spec.rb +0 -1
- data/spec/functional/final_struct_spec.rb +0 -1
- data/spec/functional/final_var_spec.rb +0 -2
- data/spec/functional/memo_spec.rb +7 -10
- data/spec/functional/option_spec.rb +0 -1
- data/spec/functional/pattern_matching_spec.rb +0 -1
- data/spec/functional/protocol_info_spec.rb +0 -2
- data/spec/functional/protocol_spec.rb +1 -3
- data/spec/functional/record_spec.rb +170 -87
- data/spec/functional/tuple_spec.rb +0 -1
- data/spec/functional/type_check_spec.rb +0 -2
- data/spec/functional/union_spec.rb +0 -1
- data/spec/functional/value_struct_spec.rb +0 -1
- metadata +14 -29
- data/doc/memo.txt +0 -192
- data/doc/pattern_matching.txt +0 -485
- data/doc/protocol.txt +0 -221
- data/doc/record.txt +0 -207
- data/doc/thread_safety.txt +0 -17
data/doc/protocol.txt
DELETED
@@ -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))
|
data/doc/record.txt
DELETED
@@ -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)
|
data/doc/thread_safety.txt
DELETED
@@ -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.
|