On 2010-09-28, at 4:04 PM, Joshua Ballanco wrote:

> On Sep 28, 2010, at 12:35 PM, Loren Segal wrote:
> 
>> We've all seen and written these methods before. They're annoying to write, they're even harder to document. Why couldn't we just do (forgive me, I'm using a different syntax to that which was proposed in this thread):
>> 
>>   def run(opts: Hash)
>>     Server.new(opts).start
>>   end
>> 
>>   def run(server: Server)
>>     server.start
>>   end
>> 
>> Before all the duck-typists go nuts, there's no reason why you couldn't specify a duck-type here.
> 
> The problem is that this proposal fundamentally misunderstand the concept of Duck typing. What if I create a class which behaves exactly as a Hash, has all the same methods with the same signatures as a Hash, but I call my class FunTimeCrazyDuckyHash? Which of these methods should be called?

If you read my next example (which you conveniently left out of your quote) you can see how duck types can be expressed. You also seemed to miss my statement (which you did quote) that ducktypes *could* indeed be used. This situation, however, is not a duck-typing situation. The Server class is not meant to "act like a hash". In the example above, the run() command takes a convenience hash and constructs a server object for you (a common idiom for Ruby factory methods).
> 
> If you're testing your arguments against a specific class, you're "Doing it wrong".

That's a little extreme. There are plenty of uses cases that require specific classes, or use inheritance instead of duck-typing to specify an interface. Ruby is still an object-oriented programming language, last time I checked. There's no reason not to merge the strengths of each paradigm.

> And all of us have done this at some point, but that still makes it wrong. The "duck type" way to write the above is:
> 
> def run(thing)
>  if thing.respond_to? :start
>    thing.start
>  else
>    Server.new(thing).start
>  end
> end

This is just as messy. You're putting dispatching logic that could easily be handled by your interpreter into your business logic. Again, the solution here could still be expressed with type annotations:

	def run(thing: #start)
	  thing.start
	end

	def run(thing)
	  Server.new(thing).start
	end

This is similar to the next example I showed. Of course this skews the discussion of typed annotations into something it's not. We're not talking about the "best" way to implement something in Ruby. There is no "one" way in Ruby, and the idea that you should never need to type by classes is as naive as the idea that you should never need to duck-type. Ruby is a useful language because it's multi-paradigm and can be used to express more than one problem-space. 

> 
> Mostly, if you find yourself doing this sort of checking too often, it's probably symptomatic of a larger issue with the structure of your code.

Again, I would disagree. There are plenty of fairly proper codebases that use the above examples a lot. Particularly for convenience methods, which Ruby libraries have plenty of-- far more than other languages I've seen.