named parameters in ruby?

Discussion in 'Ruby' started by xiMera, Oct 6, 2010.

  1. xiMera

    xiMera Guest

    Hi all,

    Ruby does not support named parameter for now as all of you know.
    There is really three solutions for this
    * hash as parameter
    * class as parameter
    * bindings

    Hash parameters:

    def do_some( params )
    a = params[ :a ]
    b = params[ :b ]
    end

    do_some :a => 1, :b =>2

    Class parameters (ugly, written in couple minutes)

    class A
    def metaclass
    class << self; self; end
    end

    def set_var( name, val )
    eval "@#{name} = val", binding
    self.metaclass.send( :define_method, name ) do
    eval "@#{name}"
    end
    end
    def set_vars( params )
    params.each{ |key,val| self.set_var( key, val ) }
    end
    end


    def do_some( params )
    a = params.a
    b = params.b
    end

    A.new
    a.set_vars( :a => 1. :b =>2 )
    do_some a

    Bindings way:

    def do_block( &block )
    eval "a=1", block.binding
    eval "b=2", block.binding
    end

    do_block( params ){
    a = eval 'a'
    b = eval 'b'
    }


    As we can see there is overtyping in all cases. We always need to map
    incoming variables into locals or write ugly accessor every time.

    So My question is:
    Is there any way to set local variables for blocks outside of block
    scope?

    IMHO nice solution will looks like this:
    def do_some( &block )
    block.binding.set_var( 'a', 1 )
    block.binding.set_var( 'b', 2 )
    block.call
    end

    do_some{
    puts a
    puts b
    }
     
    xiMera, Oct 6, 2010
    #1
    1. Advertising

  2. On Wed, Oct 6, 2010 at 6:05 PM, xiMera <> wrote:
    > Hi all,
    >
    > Ruby does not support named parameter for now as all of you know.
    > There is really three solutions for this
    > * hash as parameter
    > * class as parameter
    > * bindings
    >
    > Hash parameters:
    >
    > def do_some( params )
    > =A0a =3D params[ :a ]
    > =A0b =3D params[ :b ]
    > end
    >
    > do_some :a =3D> 1, :b =3D>2


    That's really the best solution available today.

    > Class parameters (ugly, written in couple minutes)
    >
    > class A
    > =A0 =A0def metaclass
    > =A0 =A0 =A0 =A0class << self; self; end
    > =A0 =A0end
    >
    > =A0 =A0def set_var( name, val )
    > =A0 =A0 =A0 =A0eval "@#{name} =3D val", binding
    > =A0 =A0 =A0 =A0self.metaclass.send( :define_method, name ) do
    > =A0 =A0 =A0 =A0 =A0 =A0eval "@#{name}"
    > =A0 =A0 =A0 =A0end
    > =A0 =A0end
    > =A0 =A0def set_vars( params )
    > =A0 =A0 =A0 =A0params.each{ |key,val| self.set_var( key, val ) }
    > =A0 =A0end
    > end
    >
    >
    > def do_some( params )
    > =A0a =3D params.a
    > =A0b =3D params.b
    > end
    >
    > A.new


    Where do you store result of A.new?

    > a.set_vars( :a =3D> 1. :b =3D>2 )
    > do_some a


    I don't think anybody would do this, after all it's much too
    convoluted. Also, why do you (ab)use a class for storing temporary
    state? You could as well do

    A =3D Struct.new :a, b:
    do_some A[1,2]

    or

    do_some OpenStruct.new:)a=3D>1,:b=3D>2)

    But even that is much more complicated than the Hash version. Plus,
    you do not get rid of the assignment in the block which you call
    "ugly" further on.

    > Bindings way:
    >
    > def do_block( &block )
    > =A0 eval "a=3D1", block.binding
    > =A0 eval "b=3D2", block.binding
    > end
    >
    > do_block( params ){
    > =A0a =3D eval 'a'
    > =A0b =3D eval 'b'
    > }


    Local assignments in the block are even worse. Also, this won't run
    because you pass one argument while the method expects none.

    > As we can see there is overtyping in all cases. We always need to map
    > incoming variables into locals or write ugly accessor every time.
    >
    > So My question is:
    > =A0 Is there any way to set local variables for blocks outside of block
    > scope?
    >
    > IMHO nice solution will looks like this:


    Frankly, I can't see what's nice about this approach.

    > def do_some( &block )
    > =A0block.binding.set_var( 'a', 1 )
    > =A0block.binding.set_var( 'b', 2 )
    > =A0block.call
    > end
    >
    > do_some{
    > =A0 puts a
    > =A0 puts b
    > }


    And what do you need that for? You can do this already today:

    def do_some
    yield 1,2
    end

    do_some do |a,b|
    puts a
    puts b
    end

    Your solution with the "injection" of local variables cannot work
    because the variables are not known inside the block so Ruby will
    error out. Also, it is not obvious where the values are coming from
    so reading this code will be made harder.

    I think your alternative solutions need a bit more polishing. :)

    Kind regards

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Oct 7, 2010
    #2
    1. Advertising

  3. xiMera wrote:
    > A.new
    > a.set_vars( :a => 1. :b =>2 )
    > do_some a


    I think you mean:

    a = A.new
    a.set_vars ( :a => 1, :b => 2)
    do_some a

    How is this better than

    do_some :a => 1, :b => 2

    ?

    Note that if you are using ruby 1.9, then you can also write

    do_some a: 1, b: 2

    which (from the caller's point of view) is getting pretty close to named
    parameters.

    At the callee's side, multiple assignment keeps things pretty clean:

    def do_some(v={})
    a, b, c = v[:a], v[:b], v[:c]
    end

    Or you can use values_at, which is a bit longer in this case.

    a, b, c = v.values_at:)a, :b, :c)

    Note that ruby needs to know at *parse* time that a,b and c are local
    variables, so it can reserve slots for them in the activation record,
    and this assignment achieves that.

    Any solution using eval to create local variables would be very
    inefficient, if it could be made to work at all.

    irb(main):010:0> def foo
    irb(main):011:1> eval("x = 5")
    irb(main):012:1> puts x
    irb(main):013:1> end
    => nil
    irb(main):014:0> foo
    NameError: undefined local variable or method `x' for main:Object
    from (irb):12:in `foo'
    from (irb):14
    from :0
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Oct 7, 2010
    #3
  4. > def do_some(v=3D{})
    > =C2=A0a, b, c =3D v[:a], v[:b], v[:c]
    > end


    One thing to remember about this idiom is what happens when you try
    and default the values:

    (see http://gist.github.com/616543 for the below code in a prettier format)

    def foo(hash =3D { :a =3D> 1, :b =3D> 2 })
    a =3D hash[:a]
    b =3D hash[:b]
    puts a.inspect
    puts b.inspect
    end

    foo

    #=3D>
    # 1
    # 2

    foo:)a =3D> 10)

    #=3D>
    # 10
    # nil

    So you need something like

    def foo(hash =3D {})
    hash =3D { :a =3D> 1, :b =3D> 2 }.merge(hash)
    a =3D hash[:a]
    b =3D hash[:b]
    puts a.inspect
    puts b.inspect
    end

    foo

    #=3D>
    # 1
    # 2

    foo:)a =3D> 10)

    #=3D>
    # 10
    # 2

    That whole requirement of doing merge is a little messy, I suppose;
    one reason that proper named/keyword arguments would be nice. Not too
    much of a problem, though, really.


    On Thu, Oct 7, 2010 at 9:17 PM, Brian Candler <> wrote:
    > xiMera wrote:
    >> A.new
    >> a.set_vars( :a =3D> 1. :b =3D>2 )
    >> do_some a

    >
    > I think you mean:
    >
    > =C2=A0a =3D A.new
    > =C2=A0a.set_vars ( :a =3D> 1, :b =3D> 2)
    > =C2=A0do_some a
    >
    > How is this better than
    >
    > =C2=A0do_some :a =3D> 1, :b =3D> 2
    >
    > ?
    >
    > Note that if you are using ruby 1.9, then you can also write
    >
    > =C2=A0do_some a: 1, b: 2
    >
    > which (from the caller's point of view) is getting pretty close to named
    > parameters.
    >
    > At the callee's side, multiple assignment keeps things pretty clean:
    >
    > def do_some(v=3D{})
    > =C2=A0a, b, c =3D v[:a], v[:b], v[:c]
    > end
    >
    > Or you can use values_at, which is a bit longer in this case.
    >
    > =C2=A0a, b, c =3D v.values_at:)a, :b, :c)
    >
    > Note that ruby needs to know at *parse* time that a,b and c are local
    > variables, so it can reserve slots for them in the activation record,
    > and this assignment achieves that.
    >
    > Any solution using eval to create local variables would be very
    > inefficient, if it could be made to work at all.
    >
    > irb(main):010:0> def foo
    > irb(main):011:1> eval("x =3D 5")
    > irb(main):012:1> puts x
    > irb(main):013:1> end
    > =3D> nil
    > irb(main):014:0> foo
    > NameError: undefined local variable or method `x' for main:Object
    > =C2=A0from (irb):12:in `foo'
    > =C2=A0from (irb):14
    > =C2=A0from :0
    > --
    > Posted via http://www.ruby-forum.com/.
    >
    >
     
    Adam Prescott, Oct 8, 2010
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Adam Ruth

    Named parameters

    Adam Ruth, Oct 30, 2003, in forum: C Programming
    Replies:
    48
    Views:
    1,096
    James Hu
    Nov 3, 2003
  2. Magnus Lyck?
    Replies:
    5
    Views:
    397
    Magnus Lyck?
    Dec 2, 2003
  3. John Leslie

    pass named parameters to python

    John Leslie, Feb 8, 2005, in forum: Python
    Replies:
    1
    Views:
    533
    Duncan Booth
    Feb 8, 2005
  4. Bill Pursell

    Named parameters

    Bill Pursell, Jun 21, 2006, in forum: C Programming
    Replies:
    37
    Views:
    990
    Mark McIntyre
    Jun 24, 2006
  5. Stephan Mueller
    Replies:
    7
    Views:
    635
    Stephan Mueller
    Aug 7, 2005
Loading...

Share This Page