lab42_curry 0.1.0 → 0.1.1
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/README.md +56 -15
- data/lib/lab42/curry.rb +8 -2
- data/lib/lab42/curry/arg_compiler.rb +40 -40
- data/lib/lab42/curry/data.rb +19 -8
- data/lib/lab42/curry/errors.rb +2 -0
- data/lib/lab42/curry/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 364ab07ebc3ae04d88f52078f9685de77f1ac5f7acc22c0fdc14352c80e14c93
|
4
|
+
data.tar.gz: fe68d0fa92be55bae32d4eee096ef0989a94381701e9a41a31b31c516977145b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e98215ae8b0a6ea139afe52e4f98648e351f48c950f336231002b134837b15254687649357205c6942e9659a9c3966a0464b875cc6dce949b4410857fe7979ba
|
7
|
+
data.tar.gz: 27948a35f86d517a2eb681a2d378b0d13fee608990d2f958eff1fc206551b3cfdc69d01ab09b4c2630eaf6761d9298a13bfd020eb703a1135b49be05d422c282
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
[](https://rubygems.org/gems/lab42_curry)
|
2
|
+
[](https://github.com/robertdober/lab42_curry/actions)
|
3
|
+
[](https://coveralls.io/github/RobertDober/lab42_curry?branch=master)
|
3
4
|
|
4
5
|
# Lab42::Curry
|
5
6
|
|
@@ -20,8 +21,9 @@ Given such a simple funcion
|
|
20
21
|
```ruby
|
21
22
|
def adder(a, b, c); a + 10*b + 100*c end
|
22
23
|
let(:add_to_1) {curry(:adder, 1)}
|
24
|
+
# Equivalent to Elixir's &adder(1, &1, &2)
|
23
25
|
```
|
24
|
-
**N.B.** that `Lab42::Curry` has been included into Examples **and** ExampleGroups in `spec/spec_helper.rb`
|
26
|
+
**N.B.** that `Lab42::Curry` has been included into Examples **and** ExampleGroups in `spec/spec_helper.rb`
|
25
27
|
|
26
28
|
Then very unsurprisingly:
|
27
29
|
```ruby
|
@@ -31,7 +33,7 @@ Then very unsurprisingly:
|
|
31
33
|
We call the arguments passed into `curry` the _compiletime arguments_, and the arguments passed into the
|
32
34
|
invocation of the _curried function_, which has been returned by the invocation of `curry`, the _runtime arguments_.
|
33
35
|
|
34
|
-
In our case the _compiletime arguments_ were `[1]` and the _runtime arguments_ were `[2, 3]`
|
36
|
+
In our case the _compiletime arguments_ were `[1]` and the _runtime arguments_ were `[2, 3]`
|
35
37
|
|
36
38
|
### Reordering
|
37
39
|
|
@@ -39,7 +41,8 @@ There are several methods of reordering arguments, the simplest is probably usin
|
|
39
41
|
|
40
42
|
When a placeholder is provided (`Lab42::Curry.runtime_arg` aliased as `rt_arg` )
|
41
43
|
```ruby
|
42
|
-
let(:add_to_30) { curry(:adder, rt_arg, 3)
|
44
|
+
let(:add_to_30) { curry(:adder, rt_arg, 3) }
|
45
|
+
# Equivalent to Elixir's &adder(&1, 3, &2)
|
43
46
|
```
|
44
47
|
Then we see that
|
45
48
|
```ruby
|
@@ -47,30 +50,33 @@ Then we see that
|
|
47
50
|
```
|
48
51
|
#### Total control over argument order...
|
49
52
|
|
50
|
-
... can be achieved by passing the index of the positional argument to be used into `Lab42::Curry.runtime_arg`
|
53
|
+
... can be achieved by passing the index of the positional argument to be used into `Lab42::Curry.runtime_arg`
|
51
54
|
|
52
55
|
Given the total reorder form
|
53
56
|
```ruby
|
54
|
-
let(:
|
57
|
+
let(:twohundred_three) { curry(:adder, runtime_arg(2), runtime_arg(0), 1) }
|
58
|
+
# now first argument is c (index 2) and second a (index 0) and b = 1
|
59
|
+
# Like Elixir's &adder(&2, 1, &1)
|
55
60
|
```
|
56
61
|
Then we have
|
57
62
|
```ruby
|
58
|
-
expect(
|
63
|
+
expect( twohundred_three.(2, 3) ).to eq(213)
|
59
64
|
```
|
60
65
|
|
61
|
-
#### Picking a
|
66
|
+
#### Picking a position for a compiletime argument
|
62
67
|
|
63
|
-
It might be cumbersome to write things like: `curry(..., rt_arg, rt_arg, ..., rt_arg, 42)`
|
68
|
+
It might be cumbersome to write things like: `curry(..., rt_arg, rt_arg, ..., rt_arg, 42)`
|
64
69
|
|
65
|
-
Therefore we can express the same much more concisely with `Lab42::Curry.compiletime_args`, and its alias `ct_args`
|
70
|
+
Therefore we can express the same much more concisely with `Lab42::Curry.compiletime_args`, and its alias `ct_args`
|
66
71
|
|
67
72
|
Given
|
68
73
|
```ruby
|
69
|
-
let(:
|
74
|
+
let(:twohundred) { curry(:adder, ct_args(2 => 2)) }
|
75
|
+
# same as curry(:adder, rt_arg, rt_arg, 2)
|
70
76
|
```
|
71
77
|
Then we get
|
72
78
|
```ruby
|
73
|
-
expect(
|
79
|
+
expect( twohundred.(4, 3) ).to eq(234)
|
74
80
|
```
|
75
81
|
|
76
82
|
**N.B.** that we could have defined `add_to_30` as `curry(:adder, rt_arg, 3, rt_arg)` of course
|
@@ -78,10 +84,10 @@ Then we get
|
|
78
84
|
|
79
85
|
#### Error Handling
|
80
86
|
|
81
|
-
When you indicate values for the same position multiple times
|
87
|
+
When you indicate values for the same position multiple times
|
82
88
|
Then the `ArgumentCompiler` saves you:
|
83
89
|
```ruby
|
84
|
-
expect{ curry(:adder, 1, ct_args(0 => 1))
|
90
|
+
expect{ curry(:adder, 1, ct_args(0 => 1)) }.to raise_error(Lab42::Curry::DuplicatePositionSpecification)
|
85
91
|
```
|
86
92
|
|
87
93
|
### Context With proc like objects
|
@@ -95,6 +101,41 @@ Then we will get the negative value
|
|
95
101
|
```ruby
|
96
102
|
expect( inverse.(2, 1) ).to eq(-1)
|
97
103
|
```
|
104
|
+
|
105
|
+
### Context Keyword Arguments
|
106
|
+
|
107
|
+
Given a function which takes keyword arguments like the following
|
108
|
+
```ruby
|
109
|
+
def rectangle(length, width, border: 0, color: )
|
110
|
+
[length, width, border, color]
|
111
|
+
end
|
112
|
+
let(:red_rectangle) { curry(:rectangle, color: :red) }
|
113
|
+
let(:wide_bordered) { curry(:rectangle, rt_arg, 999, border: 1) }
|
114
|
+
```
|
115
|
+
|
116
|
+
Then the red rectangle gives us
|
117
|
+
```ruby
|
118
|
+
expect( red_rectangle.(1, 2) ).to eq([1, 2, 0, :red])
|
119
|
+
expect( red_rectangle.(1, 2, border: 1) ).to eq([1, 2, 1, :red])
|
120
|
+
```
|
121
|
+
|
122
|
+
Can we override curried values, normally not
|
123
|
+
Example: cannot override
|
124
|
+
```ruby
|
125
|
+
expect{ red_rectangle.(1, 2, color: :blue) }
|
126
|
+
.to raise_error(
|
127
|
+
Lab42::Curry::DuplicateKeywordArgument,
|
128
|
+
"keyword argument :color is already defined with value :red cannot override with :blue")
|
129
|
+
```
|
130
|
+
|
131
|
+
But we can create a more lenient curry with `curry!`
|
132
|
+
```ruby
|
133
|
+
expect( curry!(:rectangle, 1, 2, color: :red ).(color: :blue) )
|
134
|
+
.to eq([1, 2, 0, :blue])
|
135
|
+
```
|
136
|
+
|
137
|
+
|
138
|
+
|
98
139
|
# LICENSE
|
99
140
|
|
100
141
|
Copyright 2020,1 Robert Dober robert.dober@gmail.com
|
data/lib/lab42/curry.rb
CHANGED
@@ -4,8 +4,14 @@ require_relative "curry/compiletime_args"
|
|
4
4
|
|
5
5
|
module Lab42
|
6
6
|
module Curry
|
7
|
-
def curry(method_or_name, *curry_time_args, &blk)
|
8
|
-
curry_data = Data.new(self, method_or_name, *curry_time_args, &blk)
|
7
|
+
def curry(method_or_name, *curry_time_args, **curry_time_kwds, &blk)
|
8
|
+
curry_data = Data.new(self, method_or_name, *curry_time_args, **curry_time_kwds, &blk)
|
9
|
+
curry_data.curried
|
10
|
+
end
|
11
|
+
|
12
|
+
def curry!(method_or_name, *curry_time_args, **curry_time_kwds, &blk)
|
13
|
+
curry_data = Data.new(self, method_or_name, *curry_time_args, **curry_time_kwds, &blk)
|
14
|
+
curry_data.allow_kwd_override = true
|
9
15
|
curry_data.curried
|
10
16
|
end
|
11
17
|
|
@@ -5,74 +5,74 @@ require_relative 'errors'
|
|
5
5
|
module Lab42
|
6
6
|
module Curry
|
7
7
|
class ArgCompiler
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
_init
|
12
|
-
ct_args.each(&method(:_set_ct_arg))
|
13
|
-
rt_args.each(&method(:_set_rt_arg))
|
14
|
-
@final.map.sort_by(&:first).map(&:last)
|
8
|
+
|
9
|
+
def compile_args rt_args
|
10
|
+
@__compiled_args__ ||= _compile_args(rt_args)
|
15
11
|
end
|
16
|
-
|
12
|
+
|
17
13
|
private
|
18
|
-
def initialize(ct_args
|
19
|
-
|
14
|
+
def initialize(ct_args)
|
15
|
+
_init
|
16
|
+
_determine_predefined_positions! ct_args
|
17
|
+
end
|
18
|
+
|
19
|
+
def _compile_args rt_args
|
20
20
|
@rt_args = rt_args
|
21
|
+
@rt_arg_positions.each(&method(:_set_rt_arg))
|
22
|
+
@rt_args.each(&method(:_set_final!))
|
23
|
+
@final.map.sort_by(&:first).map(&:last)
|
24
|
+
end
|
25
|
+
|
26
|
+
def _determine_predefined_positions! ct_args
|
27
|
+
ct_args.each(&method(:_set_ct_arg))
|
21
28
|
end
|
22
29
|
|
23
30
|
def _init
|
31
|
+
@rt_arg_positions = []
|
32
|
+
# invariant @pos is lowest free index in @final
|
33
|
+
# update both together with _set_final!( value, idx = @pos )
|
24
34
|
@pos = 0
|
25
35
|
@final = {}
|
26
36
|
end
|
27
37
|
|
28
|
-
def _set_compiletime_arg ct_arg
|
29
|
-
_store_into_next(ct_arg)
|
30
|
-
end
|
31
|
-
|
32
38
|
def _set_ct_arg ct_arg
|
33
39
|
case ct_arg
|
34
40
|
when RuntimeArg
|
35
|
-
|
41
|
+
_set_target_position! ct_arg.position
|
36
42
|
when CompiletimeArgs
|
37
|
-
_set_positions ct_arg
|
43
|
+
_set_positions! ct_arg
|
38
44
|
else
|
39
|
-
|
45
|
+
_set_final! ct_arg
|
40
46
|
end
|
41
47
|
end
|
42
48
|
|
43
|
-
def
|
44
|
-
|
49
|
+
def _set_final! value, pos=nil
|
50
|
+
pos ||= @pos
|
51
|
+
raise DuplicatePositionSpecification, "There is already a curried value for #{pos}" if @final.has_key?(pos)
|
52
|
+
@final[pos] = value
|
53
|
+
while @final.has_key?(@pos)
|
54
|
+
@pos += 1
|
55
|
+
end
|
45
56
|
end
|
46
57
|
|
47
|
-
def
|
48
|
-
|
58
|
+
def _set_position!((position, value))
|
59
|
+
_set_final! value, position
|
49
60
|
end
|
50
61
|
|
51
|
-
def
|
52
|
-
|
62
|
+
def _set_positions! ct_arg
|
63
|
+
ct_arg.each(&method(:_set_position!))
|
53
64
|
end
|
54
65
|
|
55
|
-
def
|
56
|
-
if
|
57
|
-
|
58
|
-
else
|
59
|
-
_store_into_next(rt_args.shift) # Oops I did it again
|
60
|
-
end
|
66
|
+
def _set_rt_arg rt_arg_position
|
67
|
+
raise MissingRuntimeArg, "for position #{rt_arg_position}" if @rt_args.empty?
|
68
|
+
@final[rt_arg_position] = @rt_args.shift
|
61
69
|
end
|
62
70
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
@final.has_key?(pos)
|
67
|
-
@final[pos] = value
|
71
|
+
def _set_target_position! position
|
72
|
+
@rt_arg_positions << (position || @pos)
|
73
|
+
_set_final! RuntimeArg, position
|
68
74
|
end
|
69
75
|
|
70
|
-
def _store_into_next(value)
|
71
|
-
while @final.has_key?(@pos)
|
72
|
-
@pos += 1
|
73
|
-
end
|
74
|
-
_store_at! value
|
75
|
-
end
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
data/lib/lab42/curry/data.rb
CHANGED
@@ -3,12 +3,15 @@ require_relative 'arg_compiler'
|
|
3
3
|
module Lab42
|
4
4
|
module Curry
|
5
5
|
class Data
|
6
|
-
|
6
|
+
attr_accessor :allow_kwd_override
|
7
|
+
attr_reader :context, :ct_args, :ct_kwds, :curried
|
7
8
|
|
8
9
|
private
|
9
|
-
def initialize(ctxt, method_or_name, *curry_time_args, &blk)
|
10
|
+
def initialize(ctxt, method_or_name, *curry_time_args, **curry_time_kwds, &blk)
|
11
|
+
@allow_kwd_override
|
10
12
|
@context = ctxt
|
11
13
|
@ct_args = curry_time_args
|
14
|
+
@ct_kwds = curry_time_kwds
|
12
15
|
|
13
16
|
@mthd =
|
14
17
|
case method_or_name
|
@@ -17,16 +20,24 @@ module Lab42
|
|
17
20
|
else
|
18
21
|
method_or_name
|
19
22
|
end
|
20
|
-
_curry(&blk)
|
21
|
-
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
arg_compiler.compile_args
|
24
|
+
@arg_compiler = ArgCompiler.new(ct_args)
|
25
|
+
_curry(&blk)
|
26
26
|
end
|
27
27
|
|
28
28
|
def _curry(&blk)
|
29
|
-
@curried = ->(*rt_args
|
29
|
+
@curried = ->(*rt_args, **rt_kwds) do
|
30
|
+
@mthd.(
|
31
|
+
*@arg_compiler.compile_args(rt_args),
|
32
|
+
**@ct_kwds.merge(rt_kwds) { |kwd, old_val, new_val|
|
33
|
+
if allow_kwd_override
|
34
|
+
new_val
|
35
|
+
else
|
36
|
+
raise DuplicateKeywordArgument, "keyword argument #{kwd.inspect} is already defined with value #{old_val.inspect} cannot override with #{new_val.inspect}"
|
37
|
+
end
|
38
|
+
}
|
39
|
+
)
|
40
|
+
end
|
30
41
|
end
|
31
42
|
end
|
32
43
|
end
|
data/lib/lab42/curry/errors.rb
CHANGED
data/lib/lab42/curry/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lab42_curry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Dober
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
11
|
+
date: 2020-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -80,7 +80,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
80
80
|
requirements:
|
81
81
|
- - ">="
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version:
|
83
|
+
version: 2.7.0
|
84
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - ">="
|