Ruby static typing

Some time ago, I wrote up some ideas I had for a comprehensive static
typing system for ruby. I never quite finished it all, but I'd appreciate
it if people can look it over as it is. I'd like to clean it up and put it
in an RCR (or three).

Looking back now, I see that everything is in there (tho the section on
polyparametric polymorphism could be expanded), except generic types (aka
templates) and the type conversion framework. I'll followup to this thread
with a short write-up on those when I can.

Anyway, here it is. It's kind of long, so I didn't post it to the
newsgroup, but just left it on wiki:

http://www.rubygarden.org/ruby?FlexibleRubyTypes

caleb clausen wrote:

Some time ago, I wrote up some ideas I had for a comprehensive static
typing system for ruby. I never quite finished it all, but I'd

appreciate

it if people can look it over as it is. I'd like to clean it up and

put it

in an RCR (or three).

Looking back now, I see that everything is in there (tho the section

on

polyparametric polymorphism could be expanded), except generic types

(aka

templates) and the type conversion framework. I'll followup to this

thread

with a short write-up on those when I can.

Anyway, here it is. It's kind of long, so I didn't post it to the
newsgroup, but just left it on wiki:

http://www.rubygarden.org/ruby?FlexibleRubyTypes

Static typing is proposed very often on the mailing list and usually
doesn't get very far (for good reason IMHO).

Ruby does allow you to write statically typed code as is - via a C
extension.

-Charlie

Hi --

Some time ago, I wrote up some ideas I had for a comprehensive static
typing system for ruby. I never quite finished it all, but I'd appreciate
it if people can look it over as it is. I'd like to clean it up and put it
in an RCR (or three).

Looking back now, I see that everything is in there (tho the section on
polyparametric polymorphism could be expanded), except generic types (aka
templates) and the type conversion framework. I'll followup to this thread
with a short write-up on those when I can.

Anyway, here it is. It's kind of long, so I didn't post it to the
newsgroup, but just left it on wiki:

http://www.rubygarden.org/ruby?FlexibleRubyTypes

Please look at: RCRS. The
title of the RCR is, "Should Ruby have static typing?" Under "reason
for rejection", Matz wrote, "No, it should not."

The link also has other comments, as well as links to other
discussions (though there have been many more here on ruby-talk since
then).

David

···

On Fri, 29 Apr 2005, caleb clausen wrote:

--
David A. Black
dblack@wobblini.net

"caleb clausen" <google@inforadical.net> schrieb im Newsbeitrag
news:pan.2005.04.28.20.28.58.609849@inforadical.net...

Some time ago, I wrote up some ideas I had for a comprehensive static
typing system for ruby. I never quite finished it all, but I'd

appreciate

it if people can look it over as it is. I'd like to clean it up and put

it

in an RCR (or three).

Looking back now, I see that everything is in there (tho the section on
polyparametric polymorphism could be expanded), except generic types

(aka

templates) and the type conversion framework. I'll followup to this

thread

with a short write-up on those when I can.

Anyway, here it is. It's kind of long, so I didn't post it to the
newsgroup, but just left it on wiki:

http://www.rubygarden.org/ruby?FlexibleRubyTypes

Folks, please once and for all drop the idea of static typing in Ruby.
Thank you!

Cheers

    robert

It would be nice to have a statically typed Interface of some sort
though, just to have something publishable, enforceable and easy for
tools to analyse.

···

On 4/29/05, David A. Black <dblack@wobblini.net> wrote:

The link also has other comments, as well as links to other
discussions (though there have been many more here on ruby-talk since
then).

--
Into RFID? www.rfidnewsupdate.com Simple, fast, news.

Robert Klemme wrote:

Folks, please once and for all drop the idea of static typing in Ruby.
Thank you!

I agree. Let me segway into a story related to this that I hope you will
find interesting:

During college I took an Artificial Intelligence (AI) class. In this class
the language we used for solving AI problems was Lisp. This was my first
exposure to Lisp, having previously only used Java or C in my other
classes. Other students in the class were similar.

Once I learned we would be using Lisp I took it upon myself to learn more
about the language and it's idioms, etc. This is just a habit of mine that
has proved very useful over the years as I've tried new languages.

Our main project for the class was an AI to solve a fairly simple game. I
was able to complete this project fairly easily and had fun. I used Lisp
idioms (tail recursion, simple functions, etc.) and in the end I had a
well-written, fairly small program that solved the problem well. Others
who also learned the Lisp idioms faired similarly. But there were several
students in the class who, for whatever reason (laziness,
close-mindedness, naivete) decided to write their Lisp AI program using a
style reminiscent of Java (essentially procedural, even though Lisp is a
functional language.) In one example the resulting program was probably 3
times as large as mine, ugly, hard to read, and to make matters worse, it
didn't even solve the game correctly! Now maybe this was just a bad
student, but I think the biggest problem was his inability to learn and
use Lisp idioms.

The moral? When developing in a certain programming language, the most
efficiency and ease of use can be had when following the languages design
and idioms. Ruby is dynamically typed, and trying to force it to a static
typing paradigm is going against the language and its idioms.

When coding Java, use the Java Way.
When coding C++, use the C++ Way.
When coding Ruby, use the Ruby Way.

Simple as that :slight_smile:

Regards,
Ryan

Hi --

The link also has other comments, as well as links to other
discussions (though there have been many more here on ruby-talk since
then).

It would be nice to have a statically typed Interface of some sort
though, just to have something publishable, enforceable and easy for
tools to analyse.

I question whether a tool can analyze the capabilities of an object
whose capabilities are subject to runtime-only changes. To the extent
that the tool checks for class/module membership and ancestry (as
opposed to type), you're only getting the illusion of "type" and
therefore the illusion of type safety. That kind of check also
discourages duck typing (of which it is essentially the opposite) and
generally operates to constrain Ruby programs to a model and set of
programming habits for which the language does not, by design, provide
very much support.

As Matz once put it (in response to another RCR about static typing):

   Ruby IS dynamic language. What you propose is to introduce static
   typing into Ruby which violates the whole idea of Ruby Way. Please,
   understand that class declaration has nothing to do with interfaces
   or with idea of types. Class is just a convenient place to place your
   your method declarations. An object can be changed dramatically during
   its lifetime and the type of the object is determined only by methods
   it responds to in any particular period of time.

One can of course ask an object what it responds to, before sending it
a message. That's a better fit, as a Ruby type-checking technique,
than class/module name-checking. It is not the same as duck typing,
though it's more in the same family of techniques. As I said in
another recent post, I personally tend to view respond_to? a kind of
"weak" duck typing: duck typing, in the sense that it deals directly
with the object's type and not with its class/module ancestry; "weak",
in the sense that it splits apart the checking for a behavior and the
requesting that behavior.

Quick Q&A, so that we don't have to go through them one by one :slight_smile:

   Q: Isn't changing an object's behavior dynamically bad practice?
      to an object is as easy as: "def obj.meth; end" The idea that
      this is bad is based on considerations extrinsic to Ruby.

   Q: Do that many objects *really* get changed so that their classes
      don't match their type any more?
      should be free to explore (since it's the basis of the design
      of Ruby).

   Q: Isn't Matz talking about adding some kind of
      type-checking/filtering functionality to Ruby 2.0?
      stage. Presumably it won't be a classname checking system :slight_smile:

David

···

On Fri, 29 Apr 2005, Lyndon Samson wrote:

On 4/29/05, David A. Black <dblack@wobblini.net> wrote:

   A: No. That's what #extend is for, and that's why adding a method
   A: Some -- and it's always possible, and it's a technique people
   A: Yes. As far as I know it's still at the very broad conceptual

--
David A. Black
dblack@wobblini.net

The moral? When developing in a certain programming language, the most
efficiency and ease of use can be had when following the languages design
and idioms. Ruby is dynamically typed, and trying to force it to a static
typing paradigm is going against the language and its idioms.

Well to be fair, I never asked for the entire language to change its
type model, just for some form of interface that could 'seal off'
large sections of functionality/code, like components.

Although they were GUI specific, Remember VBX/OCX, how sweet it was
that you could drop in 3rd party functionality into a MS IDE and the
IDE would immediatly know what the interface to be presented to the
programmer was.

Imagine trying to do the same in Ruby or another softly typed
language, A whole lot of framework code would need to be written and
maintained ( human written unit tests vs compiler enforced type
checking ).

In 'programming in the large' Loose coupling is pretty much enforced
if all you have is an interface, there is no option to 'poke around'
in object internals.

I'm not being instistent, just contemplative :slight_smile:

···

On 4/30/05, Ryan Leavengood <mrcode@netrox.net> wrote:

When coding Java, use the Java Way.
When coding C++, use the C++ Way.
When coding Ruby, use the Ruby Way.

Simple as that :slight_smile:

Regards,
Ryan

--
Into RFID? www.rfidnewsupdate.com Simple, fast, news.

I'm sure Hal would agree with that! :slight_smile:

Tom

···

On Sat, 2005-04-30 at 01:57 +0900, Ryan Leavengood wrote:

When coding Ruby, use the Ruby Way.

Thanks for the dense ( in a good way :slight_smile: ) response.

I guess my point is there is alot of mistrust in the 'corporate' world
as to whether dynamic/scripty languages are suitable for million+ LOC
projects.

Type safety seems to be a big selling point at that scale, so being
able to 'componentise' chunks of ruby by limiting coupling to using
rigid interfaces might assuage some of that concern. Of course after
the code executes post interface all bets are off with mutable
classes/objects, allthough Object.freeze probably needs a bit of
publicity here.

thanks for your response!

cheers
lyndon

···

On 4/29/05, David A. Black <dblack@wobblini.net> wrote:

--
Into RFID? www.rfidnewsupdate.com Simple, fast, news.

Lyndon Samson wrote:

Although they were GUI specific, Remember VBX/OCX, how sweet it was
that you could drop in 3rd party functionality into a MS IDE and the
IDE would immediatly know what the interface to be presented to the
programmer was.

Imagine trying to do the same in Ruby or another softly typed
language, A whole lot of framework code would need to be written and
maintained ( human written unit tests vs compiler enforced type
checking ).

This is probably a naive implementation of what you described above (and
maybe I've misinterpreted you), but it still isn't all that complicated:

class Class
  def exposed_methods
    @exposed_methods ||=
  end

  def attr_exposed(*method_symbols)
    (@exposed_methods ||= ).concat method_symbols
  end
end

class MyComponent
  attr_exposed :do_this, :do_that

  def do_this
    "do this!"
  end

  def do_that
    "do that!"
  end
end

class ComponentUser
  def ComponentUser::show_component_methods(component_class)
    puts "Exposed methods for #{component_class.name}:"
    obj = component_class.new
    component_class.exposed_methods.each do |m|
      arity = obj.method(m).arity
      puts "- #{m}(#{arity >= 0 ? arity : 'variable number of'} arguments)"
    end
  end
end

class String
  attr_exposed :, :slice, :each_byte
end

if $0 == __FILE__
  ComponentUser.show_component_methods(MyComponent)
  ComponentUser.show_component_methods(String)
  ComponentUser.show_component_methods(Array)
end

This way you can limit clients to what methods they can use based on the
exposed_methods. Of course the clients need to play along nicely, which
Ruby can't necessarily force them to do. But this begs the question,
isn't Class#instance_methods(false) essentially the same as this
exposed_methods in most cases?

Of course after writing this I realized you still don't know what the
arguments mean. But that is what ri is for. Plus even with Java or other
component frameworks you still need some information external to the
component to learn how to use it, even if you know what the static types
of the arguments are.

The results from the above exposed_methods call could be linked with ri in
a component system to ease this. Hmmm, not a bad idea...

Ryan

I question whether a tool can analyze the capabilities of an object
whose capabilities are subject to runtime-only changes. To the

extent

This is a good question. I've often expressed my approval of strong
typing, although I'm a proponent of type inferrence, rather than the
more common proposals for adding typing syntax. However, as I've often
said, a system like this would be useful to generate warnings, not
errors. If there was a typing system in Ruby, I'd have it use type
inferrence, and it would be enabled only when the user ran "ruby -c" on
their sources. It would generate warnings like:

  WARNING: foo.rb:15: diddle.parse
        'diddle' originally defined in babble.rb:24 as an Array
        foo.rb::boozle( diddle ) called from fang.rb:64

A clever implementation would print the sequence of method calls that
led to the type error:

  TYPING WARNING: foo.rb:15: diddle.parse
        'diddle' originated in babble.rb:24 as Array
          babble.rb:24 -> geezer.rb::thang()
          geezer.rb:18 -> homer.rb::fink()
          home.rb:23 -> lala.rb::trixie()
          lala.rb:84 -> foo.rb::boozle()

Aside from typos, this is the most common sort of error in *all* of my
Ruby applications. Through a series of method calls, an object that I
think answers to some method isn't the object that I think it is. I've
had code that passes all of the unit tests I can think to write, and
gets used for a week *before* an error of this type up. These are the
most common sleeper errors I encounter.

Again, in some Ruby programs where the objects have methods dynamically
added, you'd get false warnings. They're warnings; you look at them,
find out that they're false positives, and ignore them. If it really
annoys you, don't run the checker. It isn't unreasonable to assume
that, once you have the basic type inferrence system in place,
extending it to recognize simple cases of dynamic class/object
extension wouldn't be too difficult, and the number of false positives
would be reduced. But the main point is that we'd have an additional
tool for checking the code *before* deployment, rather than discovering
it when it is in production.

I think that people who are most afraid of strong typing are people who
were bitten by Ada, or C, or Java, or some other language where you
spend a lot of time declaring variables. They are, by and large,
people who've not had any experience with languages that have type
inferrence, like Haskell.

My standard caveat applies: type inferrence can be a tricky problem,
and requires tight coupling with the parse tree of the sourcecode. I
don't complain about not having it because I'm not willing to write it
myself, but I'm happy to voice my opinion when other people bring up
the topic :slight_smile:

--- SER

Lyndon Samson wrote:

I guess my point is there is alot of mistrust in the 'corporate' world
as to whether dynamic/scripty languages are suitable for million+ LOC
projects.

Type safety seems to be a big selling point at that scale, so being
able to 'componentise' chunks of ruby by limiting coupling to using
rigid interfaces might assuage some of that concern. Of course after
the code executes post interface all bets are off with mutable
classes/objects, allthough Object.freeze probably needs a bit of
publicity here.

It has been suggested before that the right way of doing interface contracts in dynamic languages is to formalize them with unit tests -- that seems to work out quite well (RubyOnRails uses unit tests for assuring that new database adapters basically work).

There is however still cases where unit tests can not replace traditional typing -- as a simple example you might have a method that behaves slightly different depending on whether its argument is enumerable or not. There is more cases where handling types at run time would be nice -- type adaption comes to mind: For example you might write a method that expects something that understands a hash-like interface (keys, fetch, store, delete) and let your users supply Arrays and so on as well.

Of course you might argue that those cases don't appear to frequently in Ruby code and that seems to be true, but it still might be useful to have idiomatic solutions for them.

So, because of the fairly common requests for static typing in Ruby, the explorations of the Python community and just generic interest I decided to code some of this up. You can find the resulting (still quite experimental) library at http://ruby-contract.rubyforge.org/

This library indeed combines unit testing with type checking and while it lets you use instance-of checks (it is generally compatible with all checks that can be used in case .. when statements) their use is discouraged in favor of actual implementation testing. (Which is like duck-typing, except that it does not only check names, but also behavior.)

Perhaps this will do what you want, but even if it doesn't: Ruby has not had static typing so far and because of that it is a more productive language IMHO. I'm not encouraging carelessly written code, but Ruby just happens to have other preferred ways of ensuring code quality.

Thanks for the dense ( in a good way :slight_smile: ) response.

"static typing does not work when your types are not static"

Anonymous classes, remove/undef_method, method_missing (and eval to
hide things even further) are all so dynamic.

As another poster put it, Ruby has other ways to get quality code.

I guess my point is there is alot of mistrust in the 'corporate' world
as to whether dynamic/scripty languages are suitable for million+ LOC
projects.

As if (void*) in C is a solution.
Adding a label doesn't fix things. In Ruby everything is an Object, that
should be enough for corporate drones.

As if Software Engineering in general is such a big success that it has
been proven that static typing is a panacea.
*cough* no silver bullet *cough*

The whole question about million+ LOC doesn't even make sense.
Use a language with e.g. S-expressions when you need to prove code.
Use a language that can be (and is) optimised when you need to crunch
numbers (perhaps on a massively parallel machine)
Use Ruby when you need flexibility.
[insert a few more demanding requirements and solutions/languages]
and then use Ruby for everything else :stuck_out_tongue:

Type safety seems to be a big selling point at that scale, so being
able to 'componentise' chunks of ruby by limiting coupling to using
rigid interfaces might assuage some of that concern. Of course after
the code executes post interface all bets are off with mutable
classes/objects, allthough Object.freeze probably needs a bit of
publicity here.

Could you use a $SAFE level for that?

+--- Kero ----------------------- kero@chello@nl ---+

all the meaningless and empty words I spoke |
                     Promises -- The Cranberries |

+--- M38c --- http://httpd.chello.nl/k.vangelder ---+