-
-
Save brixen/94262a1bf592c65e98b3 to your computer and use it in GitHub Desktop.
$ ruby -v -e 'p lambda { |a, | a }.([1, 2])' | |
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0] | |
[1, 2] | |
$ ruby -v -e 'def m(a) yield a end; p m([1, 2]) { |a, | a }' | |
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0] | |
1 | |
$ ruby -v -e 'def m(a) yield a end; l = lambda { |a, | a }; p l.([1, 2]); p m([1, 2], &l)' | |
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0] | |
[1, 2] | |
[1, 2] |
That make sense? Proc/block-literal style is like assignment style, it's loose, things don't need to match up, which lets you do things like pull key/value out of a hash as you iterate over it.
a, = [1,2]
a # => 1
And lambda/method-param style param style is much stricter, raising argument errors.
define_method :m do |a, |
a
end
m [1, 2] # => [1, 2]
As another example, behaviour of return statements would also differ between the examples.
Though, honestly, there's places where the distinction becomes very blurred:
Proc.new { |a, b, c| [a, b, c] }.curry[1][2][3] # => [1, 2, 3]
Proc.new { |a, b, c| [a, b, c] }[] # => [nil, nil, nil]
Proc.new { |a, b, c| [a, b, c] }.curry.lambda? # => false
Proc.new { |a, b, c| [a, b, c] }.curry.arity # => -1
Proc.new { |a, b, c| [a, b, c] }.curry.call # => #<Proc:0x007f9d6a89bd68>
TBH, lambdas and procs should probably be two separate classes that inherit from some hypothetical ancestor Callable (Method should inherit from it, too). Things like #curry
just don't make sense on proc style. I forgot what I was doing before, but this difference caused a bug in Surrogate.
Like, how would you even implement curry on a subclass of Proc? Curry itself is going to return an instance of Proc, not your subclass, so you have to override it, which means re-implement curry.
But if you get a proc-style, then how do you decide how many args to take?
p = Proc.new { |a, b, c:| [a, b, c] } # => #<Proc:0x007fa9b904d5c0@/var/folders/7g/mbft22555w3_2nqs_h1kbglw0000gn/T/seeing_is_believing_temp_dir20140710-82875-2ynq1s/program.rb:44>
p.parameters.map(&:first) # => [:opt, :opt, :keyreq]
p.arity # => -3
Oh right, it's proc-style, they're all optional, so the block should just get immediately invoked...
p = Proc.new { |a, b, c:| [a, b, c] } # => #<Proc:0x007fa9b904d5c0@/var/folders/7g/mbft22555w3_2nqs_h1kbglw0000gn/T/seeing_is_believing_temp_dir20140710-82875-2ynq1s/program.rb:44>
p.call 1, 2, c:4 # => [1, 2, 4]
p.curry[1, 2, c:3] # => [1, 2, 3]
p.curry[1][2, c:3] # => [1, 2, 3]
p.curry[1, 2][c:3] rescue $! # => #<ArgumentError: missing keyword: c>
p.curry[1][2] rescue $! # => #<ArgumentError: missing keyword: c>
p.curry[c:3][1][2] rescue $! # => #<ArgumentError: missing keyword: c>
O.o
What about methods? Does Curry work well with them? Nope!
blk = Proc.new { |a, b| [a, b, self] } # => #<Proc:0x007f8e3a05a5a8@/var/folders/7g/mbft22555w3_2nqs_h1kbglw0000gn/T/seeing_is_believing_temp_dir20140710-83401-1ae9co3/program.rb:57>
target = "abc" # => "abc"
target.define_singleton_method(:no_curry, &blk) # => :no_curry
target.define_singleton_method(:yes_curry, &blk.curry) # => :yes_curry
target.no_curry 1, 2 # => [1, 2, "abc"]
target.yes_curry 1, 2 # => [1, 2, main]
Clearly, curry should be on lambdas only (tbh, I wouldn't mind if it were removed from the language).
I think this is just because block literals come in as proc style, not lambda style.