Duck typing problem

​Don't confuse the function definition and the thing that calls the
function. If you had my definition above, you could then call:

    scribble Color.new(...)
    scribble :black
    scribble 'white'
    scribble Color.lookup('black')

but not:

    scribble 42​
    scribble Array.new

etc.​

···

On 3 June 2014 16:01, Roelof Wobben <r.wobben@home.nl> wrote:

Matthew Kerwin schreef op 3-6-2014 7:44:

On 3 June 2014 15:13, Roelof Wobben <r.wobben@home.nl> wrote:

Matthew Kerwin schreef op 3-6-2014 2:10:

​ A more Ruby way might be to have Color.lookup(color) be perfectly
happy to accept a Color object. Then you'd have:

   def scribble color
     color = Color.lookup color​
    ...
  end

​ For comparison, see Kernel#Integer and friends.​

thanks,
But what if someone does this

def scribble color.new
     color = Color.lookup ???
     ....
end

That's not valid Ruby code, I don't know what you're trying to represent
there. But this is what I was thinking:

   class Color
     @@names = {
      black: Color.new(0,0,0),
       white: Color.new(255,255,255),
    }
     def Color.lookup c
      case c
       when Color
        c
       when Symbol, String
         if @@names[c.to_sym]
          @@names[c.to_sym]
        else
          raise ArgumentError, "Not a color nane"
         end
      else
         raise ArgumentError, "can't convert #{c.class.name} into Color"
       end
    end
   end

   def scribble color
    color = Color.lookup color
     ...
  end

This is the opposite of duck-typing, it's effectively doing type
casting. If you call Color.lookup with a Color object, you get it back
unmodified. If you call it with no parameters, you get a generic Ruby
"ArgumentError: wrong number of arguments (0 for 1)" error.

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

Then Im confused by this test-case :

Test.assert_equals is_santa_clausable(SantaClaus.new), true

Roelof

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

> That's not valid Ruby code, I don't know what you're trying to represent
> there. But this is what I was thinking:
>
> [...]

That seems to be a pretty elegant solution.

I'd remove the "Color.lookup" code in #scribble, however. IMHO, it

introduces

alot of ambiguity: The user gets accustomed to methods that just take "a
sensible parameter", and in the end, comes up with some kind of parameter

the

programmer hasn't thought of during API design.

That's what ArgumentError is for :wink:

It's also tightly coupled to the idea of duck-typing: if any object can do
the job, it's good enough.

What about:

---%<---
class Color
        def self.(sym)
                return self.lookup sym
        end
end

# [...]

scribble Color[:black]
--->%---

What happens with: ´scribble Color[:banana]´ ?

The only big difference between yours and mine is where we write my case
statement. (Your caller has to choose what to pass to #scribble, based on
whether it has a Symbol or a Color object.)

I put the case in Color because I figure that's the expert on recognising
and extracting colour information out of unlikely places.

If Color was a simpler class, one could argue that the casting should
happen in #scribble, because that's the interface between user input and a
canvas (for example), so it's an expert in human-to-technical translation.

By pushing it out further, it could be valid, but you're making the(/every)
caller become the expert. It does allow you to create a MyColor class,
which has a similar enough API to the original Color class (i.e. it quacks
and waddles) that it could work in #scribble.. But now the author of
MyColor needs to be an expert in Color *and* #scribble, and the caller
either needs even more expertise, or faith. And if it's faith, you're back
where we started, by just throwing random objects at #scribble until one
sticks.

Sorry for hijacking this thread. I know it becomes quite philosphical this
way, but I'm very much interested in your opinions.

I think we're not far from duck typing, especially as it relates to API
design choices.

···

On Jun 3, 2014 11:15 PM, "Eric MSP Veith" <eveith@wwweb-library.net> wrote:

On Tuesday 03 June 2014 15:44:13, Matthew Kerwin <matthew@kerwin.net.au> > wrote:

Matthew Kerwin schreef op 3-6-2014
8:08:

Matthew Kerwin schreef op 3-6-2014 7:44:

                              Matthew Kerwin schreef op 3-6-2014

2:10:

A more Ruby way might be to
have Color.lookup(color) be
perfectly happy to accept a
Color object. Then you’d have:

def scribble color

                                        color = Color.lookup

color​

end

For comparison, see
Kernel#Integer and friends.​

thanks,

                            But what if someone does this



                            def scribble color.new

                                 color = Color.lookup ???

                                 ....

                            end
                        That's

not valid Ruby code, I don’t know what
you’re trying to represent there. But this
is what I was thinking:

                            class

Color

@@names = {

                                black:

Color.new(0,0,0),

white: Color.new(255,255,255),

}

def Color.lookup c

  case c

when Color

    c

when Symbol, String

                                  if

@@names[c.to_sym]

@@names[c.to_sym]

    else

raise ArgumentError, “Not a color nane”

end
  else
raise ArgumentError, "can't convert #{[c.class.name](http://c.class.name)                              } into

Color"

end

end

end

def scribble color

                              color =

Color.lookup color

end

                        This

is the opposite of duck-typing, it’s
effectively doing type casting. If you call
Color.lookup with a Color object, you get it
back unmodified. If you call it with no
parameters, you get a generic Ruby
“ArgumentError: wrong number of arguments (0
for 1)” error.


Matthew Kerwin

                          [http://matthew.kerwin.net.au/](http://matthew.kerwin.net.au/)

Then Im confused by this test-case :

            Test.assert_equals is_santa_clausable(SantaClaus.new),

true

                Roelof
        ​Don't

confuse the function definition and the thing that calls the
function. If you had my definition above, you could then
call:

              scribble

Color.new(…)

scribble :black
              scribble

‘white’

              scribble

Color.lookup(‘black’)

        but

not:

              scribble

42​

              scribble

Array.new

etc.​


Matthew Kerwin

          [http://matthew.kerwin.net.au/](http://matthew.kerwin.net.au/)
Oke, so I did this :

def is_santa_clausable(obj)

  obj.new()

end

 

but then I see this error message : NoMethodError: undefined method

`new’ for #

Also changing obj to SantaClaus gives the same error.

Roelof
···

On 3 June 2014 16:01, Roelof Wobben r.wobben@home.nl > wrote:

                        On 3 June 2014 15:13, > > > Roelof Wobben <r.wobben@home.nl> wrote:
@@names\.fetch\(c\.to\_sym\) \{|k| raise ArgumentError, &quot;Not a color name&quot; \}

This is awesome. But I tried @@names.fetch(c.to_sym, raise(ArgumentError, "Not a color name" }), which didn't work, because it is throwing an exception, for both cases *keyfound/not found*. But didn't try the block one. Don't know why block one worked, but not the other one which I tried.

···

--

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

Because a block isn't executed until later, but a regular parameter has to be evaluated before the method (in this case #fetch) is even called.

Consider:

my_object.some_method( foo, bar + 2)

You have to calculate `bar + 2` before you can pass it to #some_method

Yes it is exactly like that. For some moment I forgot that. Thanks Matthew.

Because a block isn't executed until later, but a regular parameter has to be evaluated before the method (in this case #fetch) is even called.

Consider:

my_object.some_method( foo, bar + 2)

You have to calculate `bar + 2` before you can pass it to #some_method

Yes it is exactly like that. For some moment I forgot that. Thanks Matthew.

Sure, .new is no instance method on an object. MyClass.new works and returns a
new instance of the class "MyClass".

What are you trying to do? Is this still related the original question? AFAIR,
the original problem was solved.

      --- Eric

···

On Tuesday 03 June 2014 08:46:57, Roelof Wobben <r.wobben@home.nl> wrote:

def is_santa_clausable(obj)
  obj.new()
end

but then I see this error message : NoMethodError: undefined method `new'
for #

Eric MSP Veith schreef op 3-6-2014 14:56:

···

On Tuesday 03 June 2014 08:46:57, Roelof Wobben <r.wobben@home.nl> wrote:

def is_santa_clausable(obj)
   obj.new()
end
  but then I see this error message : NoMethodError: undefined method `new'
for #

Sure, .new is no instance method on an object. MyClass.new works and returns a
new instance of the class "MyClass".

What are you trying to do? Is this still related the original question? AFAIR,
the original problem was solved.

      --- Eric

Yes, it is still related.
if that problem is solved I miss the answer somehow.
Could someone tell me where it was.

Roelof

In a nutshell: For that particular testcase/exercise, you can use e.g.
.instance_methods to check for your required methods.

The rest of the thread evolved around the discussion whether checking for an
interface in Ruby is actually good design.

HTH.

      --- Eric

···

On Tuesday 03 June 2014 15:10:06, Roelof Wobben <r.wobben@home.nl> wrote:

Yes, it is still related.
if that problem is solved I miss the answer somehow.

Eric MSP Veith schreef op 3-6-2014 15:23:

···

On Tuesday 03 June 2014 15:10:06, Roelof Wobben <r.wobben@home.nl> wrote:

Yes, it is still related.
if that problem is solved I miss the answer somehow.

In a nutshell: For that particular testcase/exercise, you can use e.g.
.instance_methods to check for your required methods.

The rest of the thread evolved around the discussion whether checking for an
interface in Ruby is actually good design.

HTH.

      --- Eric

Oke,

Im going to read the .instance_methods and hope I see how it works when I have test(roelof.new)

Roelof

Roelof Wobben schreef op 3-6-2014 16:28:

Eric MSP Veith schreef op 3-6-2014 15:23:

Yes, it is still related.
if that problem is solved I miss the answer somehow.

In a nutshell: For that particular testcase/exercise, you can use e.g.
.instance_methods to check for your required methods.

The rest of the thread evolved around the discussion whether checking for an
interface in Ruby is actually good design.

HTH.

            --- Eric

Oke,

Im going to read the .instance_methods and hope I see how it works when I have test(roelof.new)

Roelof

Sorry,. I read the whole topic again but I cannot find anything about .instance_methods or do you mean .lookup or ,responds_to.

Then I still not clear to me what I schould do when I see SantaClaus(SantaClaus.new)
Could I do SantaClaus.lookup SantaClaus.new ?

Roelof

···

On Tuesday 03 June 2014 15:10:06, Roelof Wobben <r.wobben@home.nl> >> wrote:

AFAIR, what you want to archieve is to check whether an Object responds to a
number of methods, i.e., "implements an interface". (Please not the quotation
marks, I'm still not happy with that notion.)

So, suppose your "interface" consists of the methods "method_a", "method_b"
and "method_c", you check whether a given object responds to all three.

The first, simple version can then look like this:

def test_responds_to_my_methods
	my_obj = MyClass.new
	responds_to_all = true

	[ :method_a, :method_b ].each do |method|
		responds_to_all &= my_obj.respond_to? method
	end
end

The method .instance_methods returns an Array of Symbols, each Symbol being
one method the object responds to.

You can also pass "false" as parameter to .instance_methods in order to only
include the methods the class itself defines. This allows you even better
"interface checking".

Consider:

class MyPseudoInterface
	def method_a
	end

	def method_b
	end
end

MyPseudoInterface.instance_methods(false) # => [:method_a, :method_b]

If you've got an object, you need to gain access to the class before you can
call .instance_methods. You do that using "my_obj.class", i.e.,
"my_obj.class.instance_methods".

(Notice the dot instead of the hash in the text: An instance method is
typically written using an hash as separator, e.g.,
MyClass#an_instance_method. Class methods are separated using a dot.)

Putting it all together, you can solve your exercise.

HTH.

      --- Eric

···

On Tuesday 03 June 2014 16:51:07, Roelof Wobben <r.wobben@home.nl> wrote:

Sorry,. I read the whole topic again but I cannot find anything about
.instance_methods or do you mean .lookup or ,responds_to.

Then I still not clear to me what I schould do when I see
SantaClaus(SantaClaus.new)
Could I do SantaClaus.lookup SantaClaus.new ?

Eric MSP Veith schreef op 3-6-2014 17:14:

responds_to_all &= my_obj.respond_to? method

Oke,

I now have this :

def is_santa_clausable(obj)
    responds_to_all = true
    [:say_ho_ho_ho, :distribute_gifts, :go_down_the_chimney].each do

method>

           responds_to_all &= obj.respond_to? method
    end
end

But Instead of true of false I see this [:say_ho_ho_ho, :distribute_gifts, :go_down_the_chimney] as output.
Which I find wierd. because responds_to_all is true and obj.responds_to? method schould also be true or false.

Roelof

The result of #each is the thing that was iterated over.

Enumerable#all? might be good here:

    [:say_ho_ho_ho, :distribute_gifts, :go_down_the_chimney].all? do

method>

      obj.respond_to?(method)
    end

···

On 06/03/2014 09:21 AM, Roelof Wobben wrote:

def is_santa_clausable(obj)
   responds_to_all = true
   [:say_ho_ho_ho, :distribute_gifts, :go_down_the_chimney].each do >method>
          responds_to_all &= obj.respond_to? method
   end
end

But Instead of true of false I see this [:say_ho_ho_ho, :distribute_gifts, :go_down_the_chimney] as output.
Which I find wierd. because responds_to_all is true and obj.responds_to? method schould also be true or false.

Wayne Conrad schreef op 3-6-2014 18:56:

The result of #each is the thing that was iterated over.

Enumerable#all? might be good here:

   [:say_ho_ho_ho, :distribute_gifts, :go_down_the_chimney].all? do >method>
     obj.respond_to?(method)
   end

Thanks

Finally solved this problem.

Roelof