From: ritchie@...
Date: 2018-11-17T16:54:38+00:00
Subject: [ruby-core:89846] [Ruby trunk Feature#15302] Proc#with and Proc#by, for partial function application and currying

Issue #15302 has been updated by RichOrElse (Ritchie Buitre).


shevegen (Robert A. Heiler) wrote:
> I am not sure if the API seems ok. I am also not sure if matz
> wants to have Symbols have methods such as .with(). For example,
> to me personally it is not entirely clear why "with 2" would 
> be equal to "n * 2" as such.
> 
> I am also not sure about the use case - it has not been 
> mentioned in this issue as far as I can see.
> 
> However had, perhaps we should wait a bit on the upcoming
> developer meeting this year anyway, because there have been
> other proposed changes that are somewhat related to the issue
> of how much class Symbol should be able to do - e. g. see
> what Victor Shepelev suggested, linked in to 
> https://bugs.ruby-lang.org/issues/15229 for Symbol#call.
> 
> Then we also know matz' opinion about class Symbol in
> regards to any possible changes to it.


Thank you for taking the time to review my proposal and for the suggestions.
To illustrate more clearly how **Symbol#with** works, here's another example:
~~~ ruby
[DateTime.new(2018,11,1), DateTime.new(2018,11,30)].map &:strftime.with("%m/%d/%Y")
~~~
Which is the same as the following:
~~~ ruby
[DateTime.new(2018,11,1), DateTime.new(2018,11,30)].map { |d| d.strftime("%m/%d/%Y") }
~~~

Although #15229 **Symbol#call** is shorter than the functional equivalent proposal **Symbol#with**, 
the later's interface is consistent with **Proc#with** and **Method#with** where, as you are already aware, have their method **call** already taken.

Here's a use case for *filling optional arguments*.
Given a method named **greet**:
~~~ ruby
def greet(name, greeting = 'hello')
  p "#{greeting.capitalize}! #{name}"
end

greet 'bob' # => "Hello! bob"
~~~

We can reuse the same method by pre-filling the last argument like so:
~~~ ruby
module Spanish
  GREETINGS = method(:greet).with('hola') # Using Method#call would invoke the method instead of returning a Proc.
end

Spanish::GREETINGS['Roberto'] # => "Hola! Roberto"
~~~




----------------------------------------
Feature #15302: Proc#with and Proc#by, for partial function application and currying
https://bugs.ruby-lang.org/issues/15302#change-74905

* Author: RichOrElse (Ritchie Buitre)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
**Proc#by** allows currying  implicitly
~~~ ruby
class Proc
  def by(*head)
    return self if head.none?
    curry(head.size.next).(*head)
  end
end

class Method
  def by(*head)
    to_proc.by(*head)
  end
end

class Symbol
  def by(*head)
    to_proc.by(*head)
  end
end

double = :*.by(2) # => proc { |n| 2 * n }
~~~

**Proc#with** pre-defines trailing arguments and/or block. 
~~~ ruby
class Proc
  def with(*tail, &blk)
    if arity == tail.size.next
      proc { |head| call head, *tail, &blk }
    else
      proc { |*head| call *head, *tail, &blk }
    end
  end
end

class Method
  def with(*head, &blk)
    to_proc.with(*head, &blk)
  end
end

class Symbol
  def with(*head, &blk)
    to_proc.with(*head, &blk)
  end
end

double = :*.with(2) # => proc { |n| n * 2 }
~~~

That's the basic idea, but I've also expanded on it by optimising and defining operators (+, &, |) and other methods (Proc#such) [here](https://gist.github.com/RichOrElse/12d056be5757ec7ce540708bbac2b584).




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>