Type depending methods

Hi!

- thx Matthew! -

In other languages we have interfaces:
different methods can be called with different count of args , or type of args.

In Ruby we can simulate this using one method and case.
But it must be in the original method (changing the case statement), so are there more "OO"-like ways?
(Adding a new method for a new type later, not (much) touching the original method)

thanks
Opti

​I'd say Ruby's is the **more** OO way (there is one way to send a
particular message to a particular object; if you need to send a different
message, it should be a different method.)

And if you're worried about the 'types' of the arguments, don't. "Type" is
a Bad Word™ in Ruby. We use duck typing, which is to say: if a method's
contract says it requires certain functions/methods/properties of
parameters, it doesn't matter what class a given parameter object has, as
long as it has the right methods defined.

I'm not sure what your final parenthetical means, but yeah that's what I'd
suggest. For example:

* If the method has an optional parameter, make it optional (with a default
value).
* If it behaves differently when it has two args instead of one, then those
are two different methods.
* If it behaves differently when passed something number-like instead of
something string-like, then those are two different methods.

Of course that's a very strong statement; there's no law against having a
case statement in your function to switch between different code paths
based on the interface (or class) of parameters.

Cheers

···

On 23 January 2017 at 21:29, Die Optimisten <inform@die-optimisten.net> wrote:

Hi!

- thx Matthew! -

In other languages we have interfaces:
different methods can be called with different count of args , or type of
args.

In Ruby we can simulate this using one method and case.
But it must be in the original method (changing the case statement), so
are there more "OO"-like ways?
(Adding a new method for a new type later, not (much) touching the
original method)

thanks
Opti

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

In other languages we have interfaces:
different methods can be called with different count of args , or type of
args.

In Ruby we can simulate this using one method and case.
But it must be in the original method (changing the case statement), so
are there more "OO"-like ways?
(Adding a new method for a new type later, not (much) touching the
original method)

To sum it up: you are talking about method overloading here.

I'd say Ruby's is the *more* OO way (there is one way to send a particular
message to a particular object; if you need to send a different message, it
should be a different method.)

I agree.

And if you're worried about the 'types' of the arguments, don't. "Type" is a
Bad Word™ in Ruby. We use duck typing, which is to say: if a method's
contract says it requires certain functions/methods/properties of
parameters, it doesn't matter what class a given parameter object has, as
long as it has the right methods defined.

I'm not sure what your final parenthetical means, but yeah that's what I'd
suggest. For example:

* If the method has an optional parameter, make it optional (with a default
value).
* If it behaves differently when it has two args instead of one, then those
are two different methods.
* If it behaves differently when passed something number-like instead of
something string-like, then those are two different methods.

Of course that's a very strong statement; there's no law against having a
case statement in your function to switch between different code paths based
on the interface (or class) of parameters.

It is also more efficient to not use case but use proper method
dispatch to select one of the appropriate methods. Plus, code will
become less complex.

Btw. if the topic is multiple dispatch, then there are other
techniques that can be used, e.g. a Hash with lambdas:

class X
  DISPATCH = {
    String => lambda {|this, s| puts s},
    Fixnum => lambda {|this, n| puts n.to_s}, # silly example
  }

  def m(arg)
    DISPATCH[arg.class][self, arg]
  end
end

It's not nice though.

Kind regards

robert

···

On Mon, Jan 23, 2017 at 12:49 PM, Matthew Kerwin <matthew@kerwin.net.au> wrote:

On 23 January 2017 at 21:29, Die Optimisten <inform@die-optimisten.net> > wrote:

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

Hi
It's in case you want to change (add) something later:

class Geometry # just a stupid example:
   def area(*s) # square, rectangle
     if s.length==2 then s[2]=a*b end ## would that be also ok, having no side effects?
   end
end

# Later / other developer wants to extend it:
def area(*s) # triangle
   if s.length == 3 then s*s*s
   else super(*s) # because its added afterwards, it overwrites the original method!
end # => doesn't work that easy. => How has it to be done (we don't know if the object will be a square, rect or triangle

···

On 2017-01-23 12:49, Matthew Kerwin wrote:

On 23 January 2017 at 21:29, Die Optimisten <inform@die-optimisten.net > <mailto:inform@die-optimisten.net>> wrote:

    Hi!

    - thx Matthew! -

    In other languages we have interfaces:
    different methods can be called with different count of args , or
    type of args.

I'm not sure what your final parenthetical means, but yeah that's what I'd suggest. For example:

* If the method has an optional parameter, make it optional (with a default value).
* If it behaves differently when it has two args instead of one, then those are two different methods.
* If it behaves differently when passed something number-like instead of something string-like, then those are two different methods.

Of course that's a very strong statement; there's no law against having a case statement in your function to switch between different code paths based on the interface (or class) of parameters.

Cheers
--
  Matthew Kerwin

---

Other languages:
def area(a) # square
   a*a
end
def area(a,b) # rectangle
  a*b
end
def area(a,b,c) # triangle, no problem to add later
   a*b*c
end

thanks
Opti

Thank you Robert!
Seems that's too deep for me (for now)
Why not DISPATCH[arg.class](self, arg) ? # normal parantheses

Opti

···

On 2017-01-23 14:49, Robert Klemme wrote:

Btw. if the topic is multiple dispatch, then there are other techniques that can be used, e.g. a Hash with lambdas: class X DISPATCH = { String => lambda {|this, s| puts s}, Fixnum => lambda {|this, n| puts n.to_s}, # silly example } def m(arg) DISPATCH[arg.class][self, arg] end end It's not nice though. Kind regards robert

Because that is how lambdas are invoked.

irb(main):001:0> f = lambda {|a,b| a+b}
=> #<Proc:0x00000000e436d0@(irb):1 (lambda)>
irb(main):002:0> f[1,2]
=> 3
irb(main):003:0> f(1,2)
NoMethodError: undefined method `f' for main:Object
Did you mean? fg
from (irb):3
from /usr/bin/irb:11:in `<main>'
irb(main):004:0>

Cheers

robert

···

On Mon, Jan 23, 2017 at 9:39 PM, Die Optimisten <inform@die-optimisten.net> wrote:

On 2017-01-23 14:49, Robert Klemme wrote:

Btw. if the topic is multiple dispatch, then there are other techniques
that can be used, e.g. a Hash with lambdas: class X DISPATCH = { String =>
lambda {|this, s| puts s}, Fixnum => lambda {|this, n| puts n.to_s}, # silly
example } def m(arg) DISPATCH[arg.class][self, arg] end end It's not nice
though. Kind regards robert

Thank you Robert!
Seems that's too deep for me (for now)
Why not DISPATCH[arg.class](self, arg) ? # normal parantheses

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

Other languages:
def area(a) # square
  a*a
end
def area(a,b) # rectangle
a*b
end

In OOP (not only Ruby) you would have a square object and
a rectangle object, and call their respective #area method:

square = Square.new(a)
square.area

rectangle = Rectangle.new(a, b)
rectangle.area

Let the objects handle the calculation of their areas
by defining appropriate #area methods for the Square
and Rectangle class.

def area(a,b,c) # triangle, no problem to add later
  a*b*c
end

(Off topic: that's a really strange kind of triangle...)

Note that one problem with your approach is that you can _not_
decide what shape you have, based only on the number of parameters.
A single length can correspond to a square... but what about circles?
Or rectangles and triangles: the area of both(!) is given by 2 lengths.

Generally, it's not a good idea to use your "when argument is this
type then do that..." approach, but to leave it to the objects to do
the right thing. For your example, to send them a message to do the
calculation (i.e. #area).

And if you only want to provide a Geometry module with a few simple
helper functions, you probably should define differently named methods,
as Robert already suggested.

Regards,
Marcus

···

Am 23.01.2017 um 21:37 schrieb Die Optimisten:

--
GitHub: stomar (Marcus Stollsteimer) · GitHub
PGP: 0x6B3A101A

Other languages:
def area(a) # square
   a*a
end
def area(a,b) # rectangle
  a*b
end

In OOP (not only Ruby) you would have a square object and
a rectangle object, and call their respective #area method:

square = Square.new(a)
square.area

rectangle = Rectangle.new(a, b)
rectangle.area

Doing it that way I had the problem converting the classname to the Class:
PC: Area of which item?
User: square # (PCs doesn't "know" which Classes are there)
PC: Length?
User: 5
=> PC has to find the class "Square" # maybe hash=[name->{proc}] is the best
# Therefore I tried to convert "square" to class Square
# Whats the best (programming concept) way to get that whole problem solved?

> def area(a,b,c) # triangle, no problem to add later
> a*b*c
> end
(Off topic: that's a really strange kind of triangle...)

Yeah, I've never laughed so hard!
My head infered without thinking while I was focusing on Ruby:
u=2*(a+b) => find simpler example; u = 3*s for triangle
=> area: a=a*b, and triangle a=s^3
So I hope you leniency :slight_smile:

Opti

···

On 2017-01-23 23:20, sto.mar@web.de wrote:

Am 23.01.2017 um 21:37 schrieb Die Optimisten:

Doing it that way I had the problem converting the classname to the Class:
PC: Area of which item?
User: square # (PCs doesn't "know" which Classes are there)
PC: Length?
User: 5
=> PC has to find the class "Square" # maybe hash=[name->{proc}] is
the best

If I understand your initial question and what you're actually trying to
do, you could also use String#constantize (or Object#const_get as others
suggested):

[1] pry(main)> class Square
[1] pry(main)* end
=> nil
[2] pry(main)> klass_name = 'square'
=> "square"
[3] pry(main)> klass_name.capitalize.constantize
=> Square

But this is extremely fragile, error-prone (capitalize can easily generate
an unexisting class name for example) and potentially unsafe (trusting user
input directly). What if you would want to accept, both 'triangle' and
'square triangle's? How will your user input this?

My suggestion would be to change the program to present the user the
options he may choose:

PC: Please, let me know which of the following items you want to calculate
the area of:
  1) Circle
   2) Triangle
   3) Square
   ...
User: 1

Which if you think can even be a better user experience since you clearly
see all available options and you don't have to type the full name of the
shape you want to calculate but just a number.

Once you have numbers, it's fairly simpler to validate if the input is
correct (there is a shape for the number provided) and to choose the
correct class to calculate the area.

Also, each type o shape has different attributes required to calculate its
area. You could allow each different class to embed the knowledge of 'which
parameters do I need to calculate my area?'.

Cheers
Sam

Doing it that way I had the problem converting the classname to the Class:
PC: Area of which item?
User: square # (PCs doesn't "know" which Classes are there)

that's probably wrong, the classes already be defined, I guess

PC: Length?
User: 5
=> PC has to find the class "Square" # maybe hash=[name->{proc}] is the best

`hash=[name->{proc}]' doesn't make any sense syntactically,
also for your use case it's not necessary to use lambdas.

# Therefore I tried to convert "square" to class Square
# Whats the best (programming concept) way to get that whole problem solved?

Since there are probably only a couple of shapes,
an easy start might be a simple case statement:

class Square; end
class Circle; end

choice = "SQuare" # the user input

shape = case choice.downcase
        when "s", "square"
          Square
        when "c", "circle"
          Circle
        end
  # => Square

or a hash:

shapes = {"square" => Square, "circle" => Circle}
shapes[choice.downcase] # => Square

These are not the most elegant solutions, but they are
simple and should do what you need.

Of course you could also use numbers like Samuel suggested.

Regards,
Marcus

···

Am 24.01.2017 um 23:20 schrieb Die Optimisten:

--
GitHub: stomar (Marcus Stollsteimer) · GitHub
PGP: 0x6B3A101A