beethoven 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/README.md +112 -5
- data/lib/beethoven/composable.rb +11 -0
- data/lib/beethoven/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bdf26fc9c7904a96681b438f0ea47a2061eea493
|
4
|
+
data.tar.gz: 3f5d73b121875564e705d8685c0cbad45db42cd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5b044385e4b14c01511ee53286ff51839c5a949e10b04e090dafd6dfc7702bf14f2b5b1ac5573ed8bcc58f0a1755aef5d27dcc1a4e5ee7f4f9b8a940570e770
|
7
|
+
data.tar.gz: 88b3693bf215390c9b9c74524e5d6b93eaa74a4b67fa9a0809b3dfd36bb45af746ebdd09e0b53cf995125043b3b936011a514583574e3452dbe9f89b152bd75d
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
beethoven
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.2.2
|
data/README.md
CHANGED
@@ -1,6 +1,117 @@
|
|
1
1
|
# Beethoven
|
2
2
|
|
3
|
-
|
3
|
+
Composition is an incredibly useful technique in functional programming.
|
4
|
+
I have been missing that in my development with Ruby, so I set out to implement it here.
|
5
|
+
|
6
|
+
In Haskell, you can write a function like:
|
7
|
+
|
8
|
+
```haskell
|
9
|
+
-- f is a function that takes a value of type a
|
10
|
+
-- and returns a value of type b
|
11
|
+
f :: a -> b
|
12
|
+
```
|
13
|
+
|
14
|
+
We need some analogy with Ruby concepts.
|
15
|
+
It doesn't appear to be methods, messages, or objects.
|
16
|
+
Classes, however, seem to do it nicely.
|
17
|
+
|
18
|
+
1. Replace the arrow with `new`
|
19
|
+
2. `a` is the *interface* or *duck* that fits the single parameter of the class.
|
20
|
+
3. `b` is the interface/duck that fits the object produced by new.
|
21
|
+
|
22
|
+
So, we might express a class `F` that implements message `b` and expects an object responding to `a` as:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
class F
|
26
|
+
attr_reader :b
|
27
|
+
|
28
|
+
def initialize(x)
|
29
|
+
@b = x.a
|
30
|
+
end
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
Next up, we want some class that implements the duck that `F` expects.
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class G
|
38
|
+
attr_reader :a
|
39
|
+
|
40
|
+
def initialize(x)
|
41
|
+
@a = x
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
G.new(5).a
|
46
|
+
# => 5
|
47
|
+
|
48
|
+
F.new(G.new(5)).b
|
49
|
+
# => 5
|
50
|
+
```
|
51
|
+
|
52
|
+
This is class composition. But really, it'd be a lot nicer if we could write:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
(F * G).new(5).b
|
56
|
+
# => 5
|
57
|
+
```
|
58
|
+
|
59
|
+
Or, perhaps you prefer the bash-like pipe operator and reading your compositions from left to right.
|
60
|
+
No problem:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
(G | F).new(5).b
|
64
|
+
# => 5
|
65
|
+
```
|
66
|
+
|
67
|
+
Naturally, this is quite a bit more interesting when your classes do something other than simply returning the value they were given.
|
68
|
+
In this example, the classes expect a parameter that duck-types `value`.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
class Add5
|
72
|
+
def initialize(x)
|
73
|
+
@value = x.value
|
74
|
+
end
|
75
|
+
|
76
|
+
def value
|
77
|
+
@value + 5
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Multiply10
|
82
|
+
def initialize(x)
|
83
|
+
@value = x.value
|
84
|
+
end
|
85
|
+
|
86
|
+
def value
|
87
|
+
@value * 10
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Lift
|
92
|
+
attr_reader :value
|
93
|
+
|
94
|
+
def initialize(x)
|
95
|
+
@value = x
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
(Add5 * Multiply10 * Lift).new(7).value
|
100
|
+
#=> 75
|
101
|
+
|
102
|
+
(Lift | Multiply10 | Add5).new(4).value
|
103
|
+
#=> 45
|
104
|
+
```
|
105
|
+
|
106
|
+
If you'd prefer to compose classes directly, use `Beethoven::Composer`:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
Mul10Add5 = Beethoven::Composer.new(Lift, Multiply10, Add5)
|
110
|
+
Mul10Add5.new(5).value
|
111
|
+
#=> 55
|
112
|
+
```
|
113
|
+
|
114
|
+
A more practical example is presented [here](https://gist.github.com/parsonsmatt/c1abbb830b6976566198)
|
4
115
|
|
5
116
|
## Installation
|
6
117
|
|
@@ -18,10 +129,6 @@ Or install it yourself as:
|
|
18
129
|
|
19
130
|
$ gem install beethoven
|
20
131
|
|
21
|
-
## Usage
|
22
|
-
|
23
|
-
TODO: Write usage instructions here
|
24
|
-
|
25
132
|
## Contributing
|
26
133
|
|
27
134
|
1. Fork it ( https://github.com/[my-github-username]/beethoven/fork )
|
data/lib/beethoven/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beethoven
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Parsons
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -106,6 +106,8 @@ extra_rdoc_files: []
|
|
106
106
|
files:
|
107
107
|
- ".gitignore"
|
108
108
|
- ".rspec"
|
109
|
+
- ".ruby-gemset"
|
110
|
+
- ".ruby-version"
|
109
111
|
- Gemfile
|
110
112
|
- Guardfile
|
111
113
|
- LICENSE.txt
|
@@ -114,6 +116,7 @@ files:
|
|
114
116
|
- beethoven.gemspec
|
115
117
|
- lib/beethoven.rb
|
116
118
|
- lib/beethoven/class.rb
|
119
|
+
- lib/beethoven/composable.rb
|
117
120
|
- lib/beethoven/composer.rb
|
118
121
|
- lib/beethoven/version.rb
|
119
122
|
- spec/beethoven/composer_spec.rb
|
@@ -140,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
143
|
version: '0'
|
141
144
|
requirements: []
|
142
145
|
rubyforge_project:
|
143
|
-
rubygems_version: 2.4.
|
146
|
+
rubygems_version: 2.4.6
|
144
147
|
signing_key:
|
145
148
|
specification_version: 4
|
146
149
|
summary: Make it a bit easier to compose classes in Ruby
|