Project

General

Profile

Actions

Feature #9548

closed

Module curry

Added by Anonymous about 10 years ago. Updated about 10 years ago.

Status:
Feedback
Assignee:
-
Target version:
-
[ruby-core:60953]

Description

I would like to beg for either Module#curry method with syntax

module Foo
  curry( :sym2, :sym1, 0 => 42, 1 => 43, 3 => 44, foo: "bar" )
end

or curry directive similar to the existing alias directive

module Foo
  curry sym2 sym1( 42, 43, *, 44, foo: "bar )
end

Example usage:

module MyMath
  def power a, b
    a ** b
  end

  curry square power( *, 2 )
  curry square_root power( *, 0.5 )
  curry cube( *, 3 )
  curry pow2 power( 2, * )
  curry pow_e power( Math::E, * )
  curry pow10 power( 10, * )
end

Updated by srawlins (Sam Rawlins) about 10 years ago

Firstly, Proc has a #curry, though not as flexible as you would like: http://rubydoc.info/stdlib/core/Proc#curry-instance_method

Secondly, to implement such flexibility is not too hard in pure Ruby. Here is an implementation of arbitrary currying for Procs:

def curry(prc, *args)
  Proc.new { |*remaining| prc.call(*args.map { |arg| arg || remaining.shift }) }
end

and one for Methods:

def curry_m(m, n, *args)
  define_method(m, curry(method(n), *args))
end

Thusly, you can do:

power = ->(a,b){ "#{a}**#{b} is #{a**b}" }
square = curry power, nil, 2
pow2   = curry power, 2, nil

puts "3 squared is #{square.call(3)}"  #=> 3 squared is 9
puts "two to the 3 is #{pow2.call(3)}" #=> two to the 3 is 8

list = ->(a,b,c,d) { "#{a}, #{b}, #{c}, #{d}" }
static_a   = curry list, :hi, nil, nil, nil
static_b_c = curry list, nil, :sam, :adam, nil

puts "static_a: #{static_a.call(:b, :c, :d)}" #=> static_a: hi, b, c, d
puts "static_b_c: #{static_b_c.call(:a, :d)}" #=> static_b,c: a, sam, adam, d

def power_m(a,b); a**b; end
curry_m(:square_m, :power_m, nil, 2)
curry_m(:pow2_m, :power_m, 2, nil)

puts "3 squared is #{square_m(3)}"  #=> 3 squared is 9
puts "two to the 3 is #{pow2_m(3)}" #=> two to the 3 is 8

gist: https://gist.github.com/srawlins/9146528

Updated by mame (Yusuke Endoh) about 10 years ago

That is not "currying", but "partial application."

http://lambda-the-ultimate.org/node/2266

--
Yusuke Endoh

Updated by Anonymous about 10 years ago

@mame (Yusuke Endoh), I don't quite get these nuances between "currying" and "partial application". I could try and
learn them, but my approach is different here. I do not want to learn. I want to be stubborn like
Amazonians when first confronted with Bible. Pragmatically, partial application is all I need. And
since it's long, so let the word be curry. Whole point of using this hunger evoking word (Anglosaxon
surnames notwithstanding) is because it's short and unique, like "quark". And, a shameful secret,
my mnemonic for curry is, oh, it's like with food, there is something (mutton, chicken, veggies...),
and you can add different types of curry to it. And #curry decides about curry, and I then decide
about something. So it's "partial application". I suggest to cannibalize the theorists and settle
for this naïve interpretation of curry. (Just in case that I am really missing something here,
the theorists can go find a different word for that something after we steal curry from them and
use it for "partial application".)

@sam, see #7939 for my take on what Proc#curry should do. Your example implementation
precludes the use of falsey values for currying (in the naïve sense of the word).

Updated by Anonymous about 10 years ago

I have just learned about the existence of Method#parameters method, which would enable to write near-perfect Module#curry without asking for core-level syntax on par with alias. The supporting facts for this feature request are thus slightly weaker than I thought when writing it.

Updated by matz (Yukihiro Matsumoto) about 10 years ago

  • Status changed from Open to Feedback

I don't think the term 'curry' is a proper name for the sought behavior.
In addition, I cannot think of a use-case for the feature.
Any concrete example?

Matz.

Updated by Anonymous about 10 years ago

Indeed, there is much less use for currying than for chaining in the functional world. (In #9552, I now prefer #chain to #map!.) Yet there is already Proc#curry in Ruby (whose present behavior is horrible, imo), therefore it is fair to consider giving the same convenience to the functions employed in modules as methods.

As for the name, in this particular case, I would still argue in favor of Module#curry. My list of imagined possibilities:

#curry --- 5
#partial_application --- 1 --- too long
#partial_apply --- 1
#partially_apply --- 0
#partial --- 2
#filter --- 0 --- means too many things

Should it be decided that this feature on Module is desirable at all, I would like to pretty please you to give your nod to misuse the word curry for it.

As for the usecases, again, there are less of them than for chaining, but let me make up some real-world example which would be of use in my code:

  class Simulation
    def initialize( initial_marking: {},
                    marking_clamps: {},
                    method: :pseudo_euler,
                    step: 1.0,
                    target_time: 60.0,
                    sampling: 5.0,
                    guarded: false )
      # object initialization
    end

    # class assets
  end

  class Net
    # class assets
    
    def simulation **named_args
      Simulation.new **named_args
    end

    curry euler simulation( method: :euler, ** )
    curry pseudo_euler simulation( method: :pseudo_euler, ** )
    curry runge_kutta simulation( method: :runge_kutta, ** )
    curry gillespie simulation( method: :gillespie, step: nil, ** )
    # Gillespie method computes its own step length.
  end

net = Net.new

# And then, instead of
sim = net.simulation( method: :gillespie,
                      initial_marking: { A: 100, B: 200 },
                      # etc.
                    )

# one could just type
sim = net.gillespie( initial_marking: { A: 100, B: 200 },
                     # etc.
                   )

So my mnemonic is, here we have simulation with curry named "euler", and then another one with curry named "runge_kutta", etc. (Note that I have simplified the above example, in the real code, Net instances own each a parametrized subclass of Simulation.)

Updated by Anonymous about 10 years ago

Having studied up the theory, I have added a feature request that would enable to misuse curry for partial application, #9620.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0