funkify 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +26 -9
- data/Rakefile +28 -0
- data/lib/funkify.rb +45 -15
- data/lib/funkify/version.rb +1 -1
- data/spec/funkify_spec.rb +39 -9
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1509b7e4e421ff184a3ddebf8bae52b54063883
|
4
|
+
data.tar.gz: 30e1768e67a4a65de187b0035050c795fa572e61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0ba7e0c32a65562b351b001264506ed55c13980382c25169dd76839cc23feb8138df7715aaf0cc9b8e000fe3cb56277bbc6684a3f2674f9e0263bf121594d58
|
7
|
+
data.tar.gz: e22735ce17f3f5e11ef60952c176d9d07ce1cb71f99370043035c0314e9998af8ba6b31ebf0e7f55760651713cba23dac33986947addedd82e1c05e77cd518c1
|
data/README.md
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
|
3
3
|
_Haskell-style partial application and composition for Ruby methods_
|
4
4
|
|
5
|
-
|
6
5
|
"In computer science, partial application refers to the process of
|
7
6
|
fixing a number of arguments to a function, producing another function of smaller arity."
|
8
7
|
--[Wikipedia](http://en.wikipedia.org/wiki/Partial_application)
|
9
8
|
|
10
|
-
[
|
11
|
-
[
|
9
|
+
[Curring in Haskell](http://www.haskell.org/haskellwiki/Currying)<br>
|
10
|
+
[Partial application in Haskell](http://www.haskell.org/haskellwiki/Partial_application)<br>
|
11
|
+
[Function composition in Haskell](http://www.haskell.org/haskellwiki/Function_composition)
|
12
12
|
|
13
13
|
|
14
14
|
## Usage
|
@@ -38,6 +38,8 @@ class MyFunkyClass
|
|
38
38
|
end
|
39
39
|
```
|
40
40
|
|
41
|
+
### Partial application and currying
|
42
|
+
|
41
43
|
When a method supports autocurrying it can still be invoked normally (if all parameters are provided) however if less than the required number are given a `Proc` is returned
|
42
44
|
with the given parameters partially applied:
|
43
45
|
|
@@ -49,18 +51,29 @@ add_1 = funky.add(1) #=> The `1` is partially applied and a `Proc` is returned
|
|
49
51
|
add_1.(2) #=> We invoke that `Proc` with the remaining argument and the final result (`3`) is returned.
|
50
52
|
```
|
51
53
|
|
52
|
-
|
54
|
+
### Function composition
|
55
|
+
|
56
|
+
We compose methods using the `*` and `|` operators.
|
53
57
|
|
58
|
+
`*` composes right to left, this is the standard way to compose functions found in languages like Haskell:
|
54
59
|
```ruby
|
55
|
-
|
56
|
-
add_1_and_multiply_by_5.(10) #=> 55
|
60
|
+
(mult(5) * add(1)).(10) #=> 55
|
57
61
|
|
58
|
-
# We can
|
62
|
+
# We can further compose the above with another method:
|
63
|
+
(negate * mult(5) * add(1)).(10) #=> -55
|
64
|
+
```
|
59
65
|
|
60
|
-
|
66
|
+
`|` composes left to right, like a shell pipeline:
|
67
|
+
```ruby
|
68
|
+
(mult(5) | add(1) | negate).(3) #=> -16
|
61
69
|
```
|
62
70
|
|
63
|
-
|
71
|
+
As a cute bonus, we can inject values from the left into a pipeline with the `pass` method ([see more](http://showterm.io/47f46234281cf2c25f44a#fast)):
|
72
|
+
```ruby
|
73
|
+
pass(3) | (mult(5) | add(1) | negate) #=> -16
|
74
|
+
```
|
75
|
+
|
76
|
+
#### Other examples:
|
64
77
|
|
65
78
|
Add 10 to every item in an Enumerable:
|
66
79
|
|
@@ -87,6 +100,10 @@ And then execute:
|
|
87
100
|
Or install it yourself as:
|
88
101
|
|
89
102
|
$ gem install funkify
|
103
|
+
|
104
|
+
## Dedication
|
105
|
+
|
106
|
+
This library was inspired in part by stimulating conversations with [epitron](https://github.com/epitron) on Freenode.
|
90
107
|
|
91
108
|
## Contributing
|
92
109
|
|
data/Rakefile
CHANGED
@@ -8,6 +8,34 @@ def run_specs paths
|
|
8
8
|
exec "bacon -Ispec -rubygems #{quiet} #{paths.join ' '}"
|
9
9
|
end
|
10
10
|
|
11
|
+
desc "run tests"
|
11
12
|
task :test do
|
12
13
|
run_specs Dir['spec/**/*_spec.rb'].shuffle!
|
13
14
|
end
|
15
|
+
|
16
|
+
desc "run pry with the development version of funkify loaded"
|
17
|
+
task :pry do
|
18
|
+
sh "pry -I./lib -r funkify"
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "remove all gems"
|
22
|
+
task :rm_gems do
|
23
|
+
sh "rm *.gem" rescue nil
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "build the gem"
|
27
|
+
task :gem => :rm_gems do
|
28
|
+
sh "gem build funkify.gemspec"
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "display the current version"
|
32
|
+
task :version do
|
33
|
+
puts Funkify::VERSION
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "build and push latest gems"
|
37
|
+
task :pushgem => :gem do
|
38
|
+
Dir["*.gem"].each do |gemfile|
|
39
|
+
sh "gem push funkify-#{Funkify::VERSION}.gem"
|
40
|
+
end
|
41
|
+
end
|
data/lib/funkify.rb
CHANGED
@@ -9,6 +9,14 @@ module Funkify
|
|
9
9
|
def *(other)
|
10
10
|
Funkify.compose(self, other)
|
11
11
|
end
|
12
|
+
|
13
|
+
def |(other)
|
14
|
+
if arity.zero?
|
15
|
+
other.(*self.())
|
16
|
+
else
|
17
|
+
Funkify.compose(other, self)
|
18
|
+
end
|
19
|
+
end
|
12
20
|
end
|
13
21
|
|
14
22
|
module_function
|
@@ -22,6 +30,11 @@ module Funkify
|
|
22
30
|
end
|
23
31
|
end
|
24
32
|
|
33
|
+
def pass(*xs)
|
34
|
+
-> { xs }
|
35
|
+
end
|
36
|
+
public :pass
|
37
|
+
|
25
38
|
def compose(*args)
|
26
39
|
head, *tail = args
|
27
40
|
head = _procify(head)
|
@@ -41,22 +54,22 @@ module Funkify
|
|
41
54
|
end
|
42
55
|
end
|
43
56
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
return
|
55
|
-
end
|
57
|
+
def self.auto_curry_all_methods(receiver)
|
58
|
+
in_use = nil
|
59
|
+
receiver.define_singleton_method(:method_added) do |name|
|
60
|
+
return if in_use
|
61
|
+
in_use = true
|
62
|
+
receiver.auto_curry name
|
63
|
+
in_use = false
|
64
|
+
end
|
65
|
+
end
|
56
66
|
|
57
|
-
|
58
|
-
|
59
|
-
|
67
|
+
def self.auto_curry_some_methods(names, receiver)
|
68
|
+
names.each do |name|
|
69
|
+
m = receiver.instance_method(name)
|
70
|
+
curried_method = nil
|
71
|
+
|
72
|
+
receiver.class_eval do
|
60
73
|
define_method(name) do |*args|
|
61
74
|
curried_method ||= m.bind(self).to_proc.curry
|
62
75
|
curried_method[*args]
|
@@ -64,4 +77,21 @@ module Funkify
|
|
64
77
|
end
|
65
78
|
end
|
66
79
|
end
|
80
|
+
|
81
|
+
module ClassMethods
|
82
|
+
def auto_curry(*names)
|
83
|
+
if names.empty?
|
84
|
+
Funkify.auto_curry_all_methods(self)
|
85
|
+
else
|
86
|
+
Funkify.auto_curry_some_methods(names, self)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def point_free(&block)
|
91
|
+
-> (*args) do
|
92
|
+
b = instance_exec(&block).curry
|
93
|
+
args.empty? ? b : b[*args]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
67
97
|
end
|
data/lib/funkify/version.rb
CHANGED
data/spec/funkify_spec.rb
CHANGED
@@ -105,20 +105,50 @@ describe Funkify do
|
|
105
105
|
end.new
|
106
106
|
end
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
|
108
|
+
describe "normal composition" do
|
109
|
+
it 'returns a new Proc when composing methods' do
|
110
|
+
(@c.negate * @c.plus_1).is_a?(Proc).should == true
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'invokes composed methods in the correct order (right-to-left)' do
|
114
|
+
(@c.negate * @c.plus_1).(5).should == -6
|
115
|
+
end
|
111
116
|
|
112
|
-
|
113
|
-
|
117
|
+
it 'can compose partially applied methods' do
|
118
|
+
(@c.add(5) * @c.mult(2)).(5).should == 15
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'can compose multiple methods' do
|
122
|
+
(@c.negate * @c.add(5) * @c.mult(5)).(5).should == -30
|
123
|
+
end
|
114
124
|
end
|
115
125
|
|
116
|
-
|
117
|
-
|
126
|
+
describe "reverse composition" do
|
127
|
+
it 'returns a new Proc when composing methods' do
|
128
|
+
(@c.negate | @c.plus_1).is_a?(Proc).should == true
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'invokes reverse-composed methods in the correct order (left-to-right)' do
|
132
|
+
(@c.negate | @c.plus_1).(5).should == -4
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'can reverse-compose partially applied methods' do
|
136
|
+
(@c.add(5) | @c.mult(2)).(5).should == 20
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'can reverse-compose multiple methods' do
|
140
|
+
(@c.negate | @c.add(5) | @c.mult(5)).(5).should == 0
|
141
|
+
end
|
118
142
|
end
|
119
143
|
|
120
|
-
|
121
|
-
|
144
|
+
describe "pass method" do
|
145
|
+
it 'passes values into a reverse-composition stream' do
|
146
|
+
(@c.pass(5) | ( @c.add(5) | @c.mult(5))).should == 50
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'passes values into a normal-composition stream' do
|
150
|
+
(@c.pass(5) | ( @c.add(5) * @c.mult(5))).should == 30
|
151
|
+
end
|
122
152
|
end
|
123
153
|
end
|
124
154
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: funkify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Mair
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|