Typed Parameters


#1

Dear group

there was a recent thread about “Boolean” and it braught me to my
favorit
thing I would love to have in Ruby2.

typed parameters

My code is completely filled with things like this:

def never_saw_a_better_method( clever, smart, stupid )
assert_type clever, String
assert_type smart, Hash
assert_type stuoid, Boolean

def cool( clever : String, smart : Hash, stupid : Boolean )

and of course
def x(y) would be equivalent to def(x : Object )

There is no other than practical reason to that, I found that this kind
of
type assertion finds the most devious bugs in my code.

Thaughts?

Robert

Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

#2

On 4/1/06, Robert D. removed_email_address@domain.invalid wrote:

Dear group

there was a recent thread about “Boolean” and it braught me to my
favorit thing I would love to have in Ruby2.

typed parameters

This might happen, but it’s unlikely to work the way you have
suggested. I personally hope it doesn’t happen, as it will make
Ruby a little less “smart.”

def x(y) would be equivalent to def(x : Object )

There is no other than practical reason to that, I found that this
kind of type assertion finds the most devious bugs in my code.

It’s not actually that practical, and such things end up making your
code very much like C++ and Java.

Ruby is smarter than that. Ruby can do more than that.

Think in terms of what your object’s required capabilities are instead
of pretending that a class indicator is sufficient for that.

-austin


#3
It's not actually that practical, and such things end up making your code very much like C++ and Java.

Ruby is smarter than that. Ruby can do more than that.

Think in terms of what your object’s required capabilities are instead
of pretending that a class indicator is sufficient for that.

While I understand you pointr Austin --obviously where talking Duck
Typing here. But I think it is interesting to condier that this is some
respect antithetical to OOP in general --I mean the reciever is a
specific type. And that reacieve detemine the functionality of the
method call. It is sort of as if you were progamming in a more
traditional functional language and had to specifiy the type of the
first argument, but never the remaining.

foofunc( FooClass foo, clever, smart, stupid )

instead of

foo.foofunc( clever, smart, stupid )

So why shouldn’t any of the other participating objects have a
selective effect too?

T.


#4

Hi –

On Sun, 2 Apr 2006, Trans wrote:

While I understand you pointr Austin --obviously where talking Duck

foo.foofunc( clever, smart, stupid )

It’s actually more like this:

foofunc(object_that_responds_to_foofunc, etc.)

The fact that an object handles a message does not imply its class.
(I’m transliterating ‘type’ to ‘class’ as that seems to be what the
thread is actually about [as it usually is :-].)

So why shouldn’t any of the other participating objects have a
selective effect too?

Because there’s no applicable general notion of “a selective effect.”
Each of these things is a different part of a system.

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” chapters now available
from Manning Early Access Program! http://www.manning.com/books/black


#5

Trans ha scritto:

So why shouldn’t any of the other participating objects have a
selective effect too?

I agree, let's stop this antidemocratic self-proclaimed dictatorship of, well, self. (and recall that our uncle Common Lisp had dynamic typing and multiple dispatch for decades withouth becoming Java :)

#6

Well, I think you should allowed to put a selective effect on the
remaining arguments, but it should at least allow you to be a little
smarter than simply checking one single Type. I’d like to see you
able to check against multiple types as well as methods and
combinations thereof, like

def foo(arg1 : (Array and :custom_array_method) or Hash or
:special_method)

Then at least it’s simply a syntactic convenience for writing
respond_to? and kind_of? calls. And, logically, you should be able to
assign these parameter checks to a variabe so you can reduce the
duplication of them, although I don’t have a clue as to what a good
syntax for that would be… Maybe something like:

type_check = TypeCheck.new do |var|
case var
when Array
return true if var.respond_to? :custom_array_method
when Hash
return Hash
else
return true if var.respond_to? :special_method
end
return false
end

And, of course, you can do any checking you want in the block. You
could then do this:

def foo(arg1 : type_check)
def bar(arg1, arg2 : type_check)

On 4/1/06, Trans removed_email_address@domain.invalid wrote:

While I understand you pointr Austin --obviously where talking Duck

foo.foofunc( clever, smart, stupid )

So why shouldn’t any of the other participating objects have a
selective effect too?

T.


-Dan Nugent

Don’t Feel Like Typing? Send me a voicemail:
http://odeo.com/sendmeamessage/DanNugent


#7

Actually… now that I’m looking about it, that’s kinda dumb, we might
as well just add pre, post, and around calls so we can more cleanly
seperate the Type and condition checks from the actual method.

On 4/1/06, Daniel N. removed_email_address@domain.invalid wrote:

assign these parameter checks to a variabe so you can reduce the
return true if var.respond_to? :special_method
On 4/1/06, Trans removed_email_address@domain.invalid wrote:

While I understand you pointr Austin --obviously where talking Duck


-Dan Nugent

Don’t Feel Like Typing? Send me a voicemail:
http://odeo.com/sendmeamessage/DanNugent


-Dan Nugent

Don’t Feel Like Typing? Send me a voicemail:
http://odeo.com/sendmeamessage/DanNugent


#8

removed_email_address@domain.invalid wrote:

Think in terms of what your object’s required capabilities are instead

The fact that an object handles a message does not imply its class.
(I’m transliterating ‘type’ to ‘class’ as that seems to be what the
thread is actually about [as it usually is :-].)

But the class of that object dictates the functionality of that
message. That’s my point --it’s class based. A double dispath makes
this very clear:

class String
def from( obj )
# the parameter is type String no matter what
obj.to( self )
end
end

We can even do some fancy dispatching to achieve type parameters:

class String
def cool( *args )
args.shift.cool_string( self, *args )
end
end

class Hash
def cool_string( str, *args )
args.shift.cool_string_hash( str, self )
end
end

class Boolean
def cool_string_hash( str, hsh )
p str.class, hsh.class, self.class
end
end

def cool( clever, smart, stupid )
clever.cool( smart, stupid )
end

cool( “a string”, { :a=>‘hash’ }, Boolean.new )

produces

String
Hash
Boolean

but

cool( 1, 2, 3 ) # => NoMethodError

T.


#9

Daniel N. wrote:

Actually… now that I’m looking about it, that’s kinda dumb, we might
as well just add pre, post, and around calls so we can more cleanly
seperate the Type and condition checks from the actual method.

No, it’s not so dumb actually. A system like the one you propose could
be used for multi-dispatch --mere method wraps could not, they’d just
be a more formal way of doing what we already must do using case
statments.

I actually wrote a system much like teh one you propose called
LightType, it was based on the Euphoria language’s type system. Hmm…
at the time I didn’t hink of using multi-dispatch with it. Since I
recently wrote a working overload method maybe I’ll revist the
possibility.

T.


#10

Hrm… I see, however, effective multi-dispatch would still be
possible with only around calls. Simply make the public method a
dummy:

def public_dummy(*args);end.around do |*args|
SOME_ARGUMENT_CONDITION CODE
when CONDITION 1
real_method_1
when CONDITION 2
real_method_2
else
real_method_general
end
end

And, I would say that this is perfectly acceptable because the number
of times you actually NEED multi-methods in Ruby is going to be pretty
small.

On 4/1/06, Trans removed_email_address@domain.invalid wrote:

I actually wrote a system much like teh one you propose called
LightType, it was based on the Euphoria language’s type system. Hmm…
at the time I didn’t hink of using multi-dispatch with it. Since I
recently wrote a working overload method maybe I’ll revist the
possibility.

T.


-Dan Nugent

Don’t Feel Like Typing? Send me a voicemail:
http://odeo.com/sendmeamessage/DanNugent


#11

On 4/2/06, Austin Z. removed_email_address@domain.invalid wrote:

suggested. I personally hope it doesn’t happen, as it will make

Think in terms of what your object’s required capabilities are instead
of pretending that a class indicator is sufficient for that.

-austin

Austin Z. * removed_email_address@domain.invalid
* Alternate: removed_email_address@domain.invalid

Austin as I have the highes admiration to your contribution to the
community, these are claims without any evidence.
Would you care to provide some?
I’d really appreciate.

Robert


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

#12

Maybe the code for that could look something like this:

def foo(arg1, arg2)
BLAHBLAHBLAH STUFF GOES HERE
end.pre do |*args|
SOME MORE STUFF
end.post do |*result|
A LITTLE MORE
end.around do|*args, &method|
SOME
method.call(transformed_args)
STUFF
end

And then you could dynamically add and remove the pre and post
conditions or whatever.

On 4/1/06, Daniel N. removed_email_address@domain.invalid wrote:

when Array
could then do this:

first argument, but never the remaining.
T.


-Dan Nugent

Don’t Feel Like Typing? Send me a voicemail:
http://odeo.com/sendmeamessage/DanNugent


-Dan Nugent

Don’t Feel Like Typing? Send me a voicemail:
http://odeo.com/sendmeamessage/DanNugent


#13

On 4/2/06, Daniel N. removed_email_address@domain.invalid wrote:

Then at least it’s simply a syntactic convenience for writing
return Hash
def bar(arg1, arg2 : type_check)

I quite agree, showes my how helpless I braught this up. :frowning:
Should have named the thread “Easier ways to enforce contracts”.
Is that not strange that we think types immediately and than are afraid
of
ancient Computer Science History.

Now there is another point.

My programs often pass objects of an unexpected “contract, behavior,
type,
mixin interface” you name it.
I would like a modern language to help me, the stupid programmer to be
more effective
Ruby claims that.

So let me suggest the following syntax

def foo (bar, foobar) is a shortcut to
def foo( bar, foobar : Object) which is a shortcut to
def foo( bar, foobar : { |para| Object === para } ) so we can put any
constraint in there.

I still think that is a good thing for readability and maintenance and
nobody would be forced to use it.
Furthermore we are not forced to use the block inside the formal
parameter
list, it would just give us the opportunity.

I really fail to see why this is inhibiting the language.
Such programs fail explicitly and early, which is better (I claim) than
maybe failing later or just not behaving as expected.

Robert

-Dan Nugent

Don’t Feel Like Typing? Send me a voicemail:
http://odeo.com/sendmeamessage/DanNugent


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

#14

Daniel N. ha scritto:

And, I would say that this is perfectly acceptable because the number
of times you actually NEED multi-methods in Ruby is going to be pretty
small.

care to elaborate?

I don’t think you’re saying that we can’t do things withouth
multimethods, but just in case, a cpu with only
SUBTRACT_AND_BRANCH_IF_NEGATIVE is perfectly fine to do everything, but
this does not make it pleasant.


#15

Hi –

On Sun, 2 Apr 2006, Trans wrote:

Ruby is smarter than that. Ruby can do more than that.
traditional functional language and had to specifiy the type of the
foofunc(object_that_responds_to_foofunc, etc.)

The fact that an object handles a message does not imply its class.
(I’m transliterating ‘type’ to ‘class’ as that seems to be what the
thread is actually about [as it usually is :-].)

But the class of that object dictates the functionality of that
message. That’s my point --it’s class based. A double dispath makes
this very clear:

No: the definition of the method dictates the functionality of the
method. That can vary from one instance to another, even within a
class. It often doesn’t; but because it can, one can view the case
where two instances of a class behave the same way as a special case
of the case where they don’t.

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” chapters now available
from Manning Early Access Program! http://www.manning.com/books/black


#16

Hi –

On Sun, 2 Apr 2006, gabriele renzi wrote:

perfectly fine to do everything, but this does not make it pleasant.
Well, something is making Ruby pleasant, so the absence of this
other stuff can’t be too big a problem :slight_smile:

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” chapters now available
from Manning Early Access Program! http://www.manning.com/books/black


#17

On 4/2/06, Robert D. removed_email_address@domain.invalid wrote:

Then at least it’s simply a syntactic convenience for writing
return Hash
def bar(arg1, arg2 : type_check)
I quite agree, showes my how helpless I braught this up. :frowning: Should
have named the thread “Easier ways to enforce contracts”. Is that not
strange that we think types immediately and than are afraid of ancient
Computer Science History.

In Ruby, at least, inheritance is not a guarantee of a contract (even on
objects derived from core classes). It’s far better to think in terms of
what an object must do in your method, rather than to try to consider
what they are in your method. An example might help.

Transaction::Simple has a debug mechanism. It expects an object that can
respond to #<< for output (and I believe checks for it). By default,
that means that I support String, Array, IO (and most, if not all,
descendants), and StringIO. I explicitly wanted it this way, whereas if
I had placed a class check (and make no mistake: most people are
familiar with statically typed languages and the addition of this will
make people think of Ruby in terms of static typing), I would have
prevented the use of all but the class(es) that I explicitly specified.

Unless you have a damn good reason to restrict a method to a
particular class, you shouldn’t be doing any sort of checking at all
that way. Checking on method presence (e.g., #respond_to?) is more
useful (but only partially so; you should check the #arity of a method),
but even that fails if the person forgets to implement #respond_to? when
they’ve implemented #method_missing. It’s also an extra run-time check
that, well, Ruby already does for you.

Make no mistake: I’m not afraid of static typing. I just think that it’s
useless in the context of real software development. I’ve been
developing software for … a long time. Professionally a little less
than that. :wink: I really cannot think of a time when I found that static
typing did anything except get in my way.

My programs often pass objects of an unexpected “contract, behavior,
type, mixin interface” you name it. I would like a modern language to
help me, the stupid programmer to be more effective Ruby claims
that.

It’s called “unit tests.” If your programs are often passing objects
that don’t behave the way you want those objects to behave, then it’s a
bug that you should be ensuring doesn’t come out of the caller; in Ruby
it’s often far more efficient to ensure that the caller doesn’t screw up
instead of the callee doing checks … that Ruby already does. This is
one reason I’m so consistently opposed to the Eater Nil that is
periodically proposed: it changes the behaviour to silently fail instead
of noisily fail if I get something I’m not supposed to (usually a nil;
in the stuff that I’ve developed, you won’t typically get a Real Object
that doesn’t work).

So let me suggest the following syntax

def foo (bar, foobar) is a shortcut to
def foo( bar, foobar : Object) which is a shortcut to
def foo( bar, foobar : { |para| Object === para } ) so we can put any
constraint in there.

I still think that is a good thing for readability and maintenance and
nobody would be forced to use it. Furthermore we are not forced to
use the block inside the formal parameter list, it would just give us
the opportunity.

It’s exceedingly ugly. It’s also no better, in implementation terms,
than placing the contract enforcement as lines of code at the top of the
method. Except that the latter is clearer, explicit, and infinitely more
maintainable than writing a miniprogram in the parameter list. (And can
easily be extracted to a unit test where that sort of thing probably
belongs in most Ruby code.)

Don’t get me wrong: there are times when specifying a type is necessary.
Those types when you’re dealing with pure Ruby are vanishingly small.
I’m looking at implementing some metacode for PDF::Core that includes
class specification (because the PDF specification has a defined number
of object classes). If I’m interacting with SOAP, it is useful to
specify the types/classes that I’m interacting with.

(I would also suggest that if you’re going to allow a block for this
sort of thing, you need to actually allow for inter-parameter validation
as well, e.g., if bar is String then foobar must be Fixnum; if bar is
Fixnum, then foobar must be String. Of course, that gets into more
complex validation, which really does underscore my point that this
behaviour belongs in the method itself. Maybe we can have precondition
and postcondition sections of methods, but putting all this in the
parameter list is ugly and nonsensical.)

I really fail to see why this is inhibiting the language. Such
programs fail explicitly and early, which is better (I claim) than
maybe failing later or just not behaving as expected.

Ah. That’s where you’re wrong: such programs would not fail any earlier
than they do now. The singular “advantage” to statically typed languages
is that they fail during the compile stage if there’s a mismatch. Your
method still won’t fail until it is called.

-austin


#18

On Apr 2, 2006, at 9:29 AM, Austin Z. wrote:

Ruby
it’s often far more efficient to ensure that the caller doesn’t
screw up
instead of the callee doing checks …

If I remember correctly from Object Oriented Software Engineering,
Betrand Meyer specifically talks about how when pre-conditions fail, it
is the caller that has violated the contract. When a program is
written
correctly (i.e., without bugs) pre-condition testing is completely
superfluous
at run-time. I think the Eiffel compilers even have a switch to turn-
off
run-time pre-condition testing.

I’d rather see the support for pre-conditions and testing within the
context of the development environment rather than burdening the run-
time
environment. The use of annotations along with smart text editors and
test-case generators might be useful.

Gary W.


#19

On 4/2/06, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

that don’t behave the way you want those objects to behave, then
written
test-case generators might be useful.

Gary W.

Interesting points, please be aware though, that the runtime
environement
would not be burdened at all.
I was just talking about alternative syntax for things one can do
already
The Eifel idea is intriguing though.
One could run with a switch to execute pre_condition assertions

A have difficulties evaluating the role of “unit testing” in these
conceptual idea of checking conditions.

Maybe we should see programs incomplete without there test suits.

As this strikes me as a sound approach not followed often enough it
might
not be possible all the time and is not really part of the Ruby concept.

Cheers
Robert


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

#20

On 4/2/06, Austin Z. removed_email_address@domain.invalid wrote:

:special_method)
return true if var.respond_to? :custom_array_method

def foo(arg1 : type_check)
def bar(arg1, arg2 : type_check)

I quite agree, showes my how helpless I braught this up. :frowning: Should
have named the thread “Easier ways to enforce contracts”. Is that not
strange that we think types immediately and than are afraid of ancient
Computer Science History.

In Ruby, at least, inheritance is not a guarantee of a contract (even on
objects derived from core classes).

That is a very delicate point too, I strongly feel that a developper
should
know what kind of protocol an object is implementing and should be
able to
enforce this at a given point.
By extending core classes (and I love to do it BTW) she is taking great
risks.
If you do not agree, that means to me that you are probably a very very
talented designer, but do you not know error prone people like me?

It’s far better to think in terms of

what an object must do in your method, rather than to try to consider
what they are in your method. An example might help.

Transaction::Simple has a debug mechanism. It expects an object that can
respond to #<< for output (and I believe checks for it). By default,
that means that I support String, Array, IO (and most, if not all,
descendants), and StringIO. I explicitly wanted it this way, whereas if
I had placed a class check (and make no mistake: most people are
familiar with statically typed languages and the addition of this will
make people think of Ruby in terms of static typing),

might be and the way I presented it, for sure, real big mistake of mine.

I would have

prevented the use of all but the class(es) that I explicitly specified.

So you would not, why should you? There is no static typing, there is
only
the possibility of early renforcement of contract.
It might be a bad idea anyway, but I did not reach you. This is
completely
clear from your very elaborate answer, which I appreciate.
Look at it like this when we ask to check for certain conditions at
certain
points by means of assertions we have normally good reason to do so,
right???
I belive that the moment when a message is sent is often the ideal
moment to
reinforce such constraints.
def foo(x : {|para| %r{Robert is a real egghead} =~ x})
I can of course write that on my own in the next line, but it would just
be
against my laziness

Maybe it is a feeling that I need help for my shortcomings as a
programmer.
I thaught “contract enforcing mechanisms” might allow for an easier way
to
program defensively.
But I might be caught in too old paradigms, I really feel that you feel
this
way and I will take it quite seriously.

Unless you have a damn good reason to restrict a method to a

particular class, you shouldn’t be doing any sort of checking at all
that way. Checking on method presence (e.g., #respond_to?) is more
useful (but only partially so; you should check the #arity of a method),
but even that fails if the person forgets to implement #respond_to? when
they’ve implemented #method_missing. It’s also an extra run-time check
that, well, Ruby already does for you.

Meta programming might ruin it, singleton methods might ruin it, we
should
use only final classes and methods as in Java :wink:
Now you see I understand this, your reaction, it might be a very sound
one.

Make no mistake: I’m not afraid of static typing. I just think that it’s

useless in the context of real software development.

Agreed

I’ve been

developing software for … a long time. Professionally a little less
than that. :wink: I really cannot think of a time when I found that static
typing did anything except get in my way.

And you never used assertions neither as you stated above?

one reason I’m so consistently opposed to the Eater Nil that is
periodically proposed: it changes the behaviour to silently fail instead
of noisily fail if I get something I’m not supposed to (usually a nil;
in the stuff that I’ve developed, you won’t typically get a Real Object
that doesn’t work).

I take this point at 100%, I might be biased by bad design methods.

the opportunity.

It’s exceedingly ugly. It’s also no better, in implementation terms,
than placing the contract enforcement as lines of code at the top of the
method. Except that the latter is clearer, explicit, and infinitely more
maintainable than writing a miniprogram in the parameter list. (And can
easily be extracted to a unit test where that sort of thing probably
belongs in most Ruby code.)

Well I can live with this statement, I was quite aggressive too :wink:

Don’t get me wrong: there are times when specifying a type is necessary.

complex validation, which really does underscore my point that this
behaviour belongs in the method itself. Maybe we can have precondition
and postcondition sections of methods, but putting all this in the
parameter list is ugly and nonsensical.)

Well no, that was not intended , please remember that I never wanted
any
obligation to check.
I wanted a tool, and probably it is not useful, you quite convinced me.

I really fail to see why this is inhibiting the language. Such

programs fail explicitly and early, which is better (I claim) than
maybe failing later or just not behaving as expected.

Ah. That’s where you’re wrong: such programs would not fail any earlier
than they do now. The singular “advantage” to statically typed languages
is that they fail during the compile stage if there’s a mismatch. Your
method still won’t fail until it is called.

That was intened like this, it would fail when called and not maybe
three
stack levels later, but as you said already, I can enforce the contract
myself on crucial points.

-austin


Austin Z. * removed_email_address@domain.invalid
* Alternate: removed_email_address@domain.invalid

Thanks a lot for your precious time taken.

Robert


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein