Surprisingly few attempts even seem to get over the bar of understanding what currying is, and why it isn't just partial application. Now, I am not too concerned with which is "better" or anything, nor do I much care for pedantry for the sake of pedantry, but in this case it's a matter of type signatures; curry(some_function) and partial_apply(some_function, param1, param2) are fundamentally different type signatures and for that reason, not because of abstract verbal purity or something, aren't the same thing.
I mean, right out of the gate you have the problem that in most conventional languages you're looking at your first example being map(reverse)(["hello", "goodbye"]).
Whether Ruby syntax could be bent into making it work without parens (or Perl perhaps) I don't know but with all the other things going on in those syntaxes I'm sure it would be quirky as can be, possibly quirky beyond practical usefulness. And in dynamic scripting languages you'd be looking at a lot of actual function calls, so performance is going to be trashed if you use this pervasively.
For the same reason, I disagree that performance in dynamic scripting languages would suffer unduly.
This syntax wouldn't work in Ruby, because bare `reverse` is already a method call, not a reference to a method by name. Allowing method calls without parentheses is crucial to Ruby's design, where all objects are fully opaque, and every `obj.attr` looking like a getter, is just a call to an instance method `attr`. (This is, as far as I understand, the opposite to similar languages like Python/JS, where the object is a dictionary of attributes, and `obj.method` reference the attribute of type "method", while `obj.method()` is an invokation.)
This design became a huge drawback in the age of functional(ish) programming, because the simplest way for refer to a method in Ruby is `method(:its_name)` (which is also inefficient, because it creates wrapping object of class Method on the fly, there is no pre-existing first-class object), and any attempt of passing/combining methods would be cumbersome due to it.
FWIW, you can do that in Ruby:
(JSON.method(:parse) >> method(:puts)).call("[1, 2, 3]")
...which is semantically cool, but looks ridiculous.Another important trait of Ruby's design is that most of the important methods belong to their first argument, so it is not `reverse(string)` but `string.reverse`, so even to obtain a reference to a method, you need to have an object it belongs to! So you can't do that:
method(:>).call(a, b)
...because all operators are called on their first operands, so you need this this: a.method(:>).call(b)
...which will require to refer to at least a first argument of the operator, so you can't produce "argument-less comparison operation to call later".But when you get down into the weeds of what it means, it turns out to be something you can't "just" drop in. You really need to build the syntax for it from day one.
You absolutely can, if you know the specific one (that is, type) you need.
E.g.,
gt = Integer.instance_method(:>).method(:bind_call)
# gt is a two arg callable functionally equivalent to ->(a,b) { a < b } for Integer a, Any b
Of course, doing this instead of just doing: gt = -> { _1 > _2 }
loses Ruby duck-typing and dynamism with regard to the first parameter; which may be acceptable in some cases, but not others. Surprisingly few attempts even seem to get over the bar of understanding what currying is, and why it isn't just partial application.
Huh, what is the difference between currying and partial application? I always thought the terms were interchangeable.Partial application (of one arg) takes a function that is of type:
(a, b, c) -> d
And makes it: a -> (b, c) -> d
Currying makes it: a -> b -> c -> d
Ruby’s Proc#curry does magic that mostly lets you pretend its either or something in between.The first example with map reverse is very readable and an improvement over lambda syntax. The chaining example (filter on length) is horrible for readability and I greatly prefer the lambda syntax.
If you're fine with the extra syntax, yes, there are many alternatives, but they don't address the point.
> We might also start look into the concept of the “tacit programming”, which, from some point of view, is also about “not repeating the arguments,” but this would be a much longer post—while it is obscenely long already.
Note though that my analysis was mostly about "how you would do that in an established language," not "how would I design a language from scratch." And Ruby is very different from Haskell in its syntax, semantics, and typical intuitions of the language writer and reader.
So designing a solution for clearer representation of "default parameters" is quite different in a language which is built around an `object.method(argument) { block }` as its atomic phrase, and one built around `function(arguments)`. (This can turn into a discussion of which phrase structure is more elegant in general... But I believe it was already done quite a few times in a history of programming languages evolution!)
irb(main):001:0> ["foo", "bar"].map(&:reverse)
=> ["oof", "rab"]
Not precisely the same as what you've outlined, of course, but perhaps useful nonetheless. module MissionControl
#...
[Proc, Method, Binding, UnboundMethod].each_with_object(self, &:include)
end
if one were so inclined.Ruby doesn't have functions, but any one paramter block (to which any one parameter callable, and some other things, can be converted with an &) can be passed as the block parameter to map. And, yes, you can use function composition and similar operations to build up more complex callables from existing ones without an explicit new block or lambda.
> ["a","bb","ccc","dddd"].filter &(2.method(:<) << :length.to_proc)
I can't imagine a stronger case for GP's thesis. The point-free ruby rewrite is so baroque and needlessly circuitous... the extra variable is a far lesser evil, and more concise:
["a","bb","ccc","dddd"].filter { |x| x.length > 2 } ["a","bb","ccc","dddd"].grep /.../
but then, I'm a recovering perl hacker ["a","bb","ccc","dddd"].grep /.../
=> ["ccc", "dddd"]
anyone who wants to suggest that a regex is not a function can fight me irb> [[1,2],[3,4]].each_with_object(:*).map(&:reduce)
=> [2, 12]
Given the mechanics of this I wouldn't claim it meets a strict definition of point-free, but that's an iterated application of operator-like methods omitting any explicit named/positional argument references.
Edit: formatting