Typo-checking instead of static typing

Once again, static typing reared its head on the mailing list, and once
again, the knights of the Ruby table slayed the beast... but it will
return. The wheel of time continues to turn and the beast will return
again.

But really, when people talk about "static typing", what is it they
*really* want. What does static typing buy you? I'd argue it's simple.

Static typing tends to find typos and brainos in code before it's executed.

It's as simple as that. I don't need to talk about the various down sides
to static typing here, but that upside is particularly important when
there are bits of code that might very rarely be executed.

So the real question is: in a dynamic, strongly typed language like Ruby,
how do you find typos and brainos in code that your app may very rarely
use?

The stock answer here is "unit tests", but honestly, I never bought that
answer. Maybe it's because too often I deal with things that are
incredibly difficult to unit test, i.e. GUIs, external data sources,
unreliable networks, opaque 3rd-party interfaces and other things which
are very difficult to mock out. It isn't to say they aren't testable,
just that they're really hard to test well.

My open question is then: how do you find typos and brainos in seldom-run
code in a dynamic language like Ruby?

(And maybe we can use the answers next time the monster rears its head)

Ben

Ben Giddings wrote:

Once again, static typing reared its head on the mailing list, and once again, the knights of the Ruby table slayed the beast... but it will return. The wheel of time continues to turn and the beast will return again.

But really, when people talk about "static typing", what is it they *really* want. What does static typing buy you? I'd argue it's simple.

Static typing tends to find typos and brainos in code before it's executed.

It's as simple as that. I don't need to talk about the various down sides to static typing here, but that upside is particularly important when there are bits of code that might very rarely be executed.

So the real question is: in a dynamic, strongly typed language like Ruby, how do you find typos and brainos in code that your app may very rarely use?

The stock answer here is "unit tests", but honestly, I never bought that answer. Maybe it's because too often I deal with things that are incredibly difficult to unit test, i.e. GUIs, external data sources, unreliable networks, opaque 3rd-party interfaces and other things which are very difficult to mock out. It isn't to say they aren't testable, just that they're really hard to test well.

My open question is then: how do you find typos and brainos in seldom-run code in a dynamic language like Ruby?

(And maybe we can use the answers next time the monster rears its head)

As I read this, I immediately imagined parsing all the individual tokens in a set of Ruby programs, and counting the number of occurrences of each. Tokens that appear infrequently (like once) are likely to be typos.

This idea, with a little more smarts added, could be used to detect potential typos.

Curt

Once again, static typing reared its head on the mailing list, and

once

again, the knights of the Ruby table slayed the beast...

It would be more accurate to say that the Knights claimed the beast
was:

1) impossible to implement
2) insignificant
3) not really a beast

largely in that order.

--- SER

Ben Giddings wrote:

Once again, static typing reared its head on the mailing list, and once again, the knights of the Ruby table slayed the beast... but it will return. The wheel of time continues to turn and the beast will return again.

But really, when people talk about "static typing", what is it they *really* want. What does static typing buy you? I'd argue it's simple.

Static typing tends to find typos and brainos in code before it's executed.

It's as simple as that. I don't need to talk about the various down sides to static typing here, but that upside is particularly important when there are bits of code that might very rarely be executed.

The arguments I've heard for static typing rarely focus on catching finger farts. Yes, that's a part of it, but the essence seems to be a desire for some assurance that not only does code compile and that objects respond to specific messages, but that the resulting behavior will match certain expectations.

(Oh, and speed is another argument.)

So the real question is: in a dynamic, strongly typed language like Ruby, how do you find typos and brainos in code that your app may very rarely use?

-w ?
-c ?

Comprehensive unit tests?

James

Yes, unit tests and ruby -w. I wrote the thing in the 'I Rock.' post between midnight and 4am. As you can imagine, writing code tired is fraught with crazy typos, but the unit tests found them all.

When I was almost done, I spent 15 minutes on a 1 character typo (rrc in one spot, rcc in another). I didn't have a unit test for that case.

···

On 03 May 2005, at 12:27, Ben Giddings wrote:

The stock answer here is "unit tests", but honestly, I never bought that
answer. Maybe it's because too often I deal with things that are
incredibly difficult to unit test, i.e. GUIs, external data sources,
unreliable networks, opaque 3rd-party interfaces and other things which
are very difficult to mock out. It isn't to say they aren't testable,
just that they're really hard to test well.

My open question is then: how do you find typos and brainos in seldom-run
code in a dynamic language like Ruby?

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

Ben Giddings wrote:

My open question is then: how do you find typos and brainos in seldom-run code in a dynamic language like Ruby?

At the risk of being jeered: I have found perl's 'use strict' with 'my' declarations have helped keep my feet unshot in the past. It won't get everything, but misspelt variables won't pop into existance behind your back. Conversely, javascript has always spent me to deadline hell with just this sort diabolical mischief (grr).

I've always wondered why ruby doesn't have somthing similar. Presumably there's some flaw/objection to the idea of having this? But! surely this is exactly what Ben is asking for?

Also, conceivably, couldn't (optional) implicit run-time checks be added which match the declared 'type' of an assignee variable to the current type of the assigned object? No type inference, just a little extra runtime overhead (which could be switched off). And/or similar optional preconditions on method arguments?

e.g.

   Fruit var1 = Banana.new # ok
   Flesh var2 = Sausage.new # ok
   Object var3 = Hamster.new # no checks on this var
   var3 = var2 # so, Sausages are okay here
   var3 = var1 # so are Bananas
   var1 = var2 # but not here, cos Sausages aren't Fruit - bang!
   var4 = 0 # var4 isn't predeclared - bang!

Um, this seems so obvious I am expecting someone to reveal my cluelessness imminently.

Nick

Ben Giddings wrote:

Static typing tends to find typos and brainos in code before it's
executed.

My most frequent Ruby error:
"NoMethodError: undefined method `foo' for nil:NilClass"

Not a typo, and arguably anything is a braino.

Fixed by asking oneself two questions
1) How in sam hill did that get set to nil?
2) Can method bar can return nil, if blah blah foozle wimble?

···

--
J. Lambert

http://rubyforge.org/snippet/detail.php?type=snippet&id=47

Description:
Searches through all defined symbols for near misses in namespace. Hopefully some of those listed will be mispelling bugs.

Usage :-
ruby -w Mispel.rb

Will run its unit tests.

ruby -rmodule1 -rmodule2 -rmodule... Mispel.rb --cache

Will create ~/.mispel_ignore, a list of known near misses in the
system and in those modules.

If you add into your script, after all require statements...

require 'Mispel'
Mispel::problem_cases
exit(1)

It will (after a long time) print out all near misses in namespace
that are not in ~/.mispel_ignore

John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

Somewhere on the edge of a Galaxy, one of literally billions of such
galaxies, is a sun, one of literally billions of suns in that
galaxy.

Orbiting that sun is a small rock 330000 times smaller than that
sun.

This rock is covered by a very very thin scum of life. (Think 6000km
of rock followed by a meter or so of biomass.)

Amongst the millions of species in that scum are many hundreds of
thousands of types beetle and a mere handful of primates.

Surprisingly enough, this email does not originate from a beetle.

It originates from just one of the 6 billion vastly outnumbered humans.

I trust you will keep this perspective and context in mind when
reacting to this email.

···

On Wed, 4 May 2005, SER wrote:

Once again, static typing reared its head on the mailing list, and

once

again, the knights of the Ruby table slayed the beast...

It would be more accurate to say that the Knights claimed the beast
was:

1) impossible to implement

Eric Hodel wrote:

···

On 03 May 2005, at 12:27, Ben Giddings wrote:

My open question is then: how do you find typos and brainos in seldom-run
code in a dynamic language like Ruby?

Yes, unit tests and ruby -w. I wrote the thing in the 'I Rock.' post between midnight and 4am. As you can imagine, writing code tired is fraught with crazy typos, but the unit tests found them all.

When I was almost done, I spent 15 minutes on a 1 character typo (rrc in one spot, rcc in another). I didn't have a unit test for that case.

Let me put in a plug for code coverage tools in general.
Combined with good unit tests, these are truly invaluable.

There's a good one out there, but I forget what it's called --
<admission>I don't use it much, I've only played with it.</admission>

Hal

Nick Woolley wrote:

Also, conceivably, couldn't (optional) implicit run-time checks be added which match the declared 'type' of an assignee variable to the current type of the assigned object? No type inference, just a little extra runtime overhead (which could be switched off). And/or similar optional preconditions on method arguments?

e.g.

  Fruit var1 = Banana.new # ok
  Flesh var2 = Sausage.new # ok
  Object var3 = Hamster.new # no checks on this var
  var3 = var2 # so, Sausages are okay here
  var3 = var1 # so are Bananas
  var1 = var2 # but not here, cos Sausages aren't Fruit - bang!
  var4 = 0 # var4 isn't predeclared - bang!

Um, this seems so obvious I am expecting someone to reveal my cluelessness imminently.

It's not a question of cluelessness, but I would state it
this way: Absence of explicit typing and variable declarations
are two of Ruby's best features, and they are foundational in
its design.

Hal

SER wrote:

Once again, static typing reared its head on the mailing list, and
once again, the knights of the Ruby table slayed the beast...

It would be more accurate to say that the Knights claimed the beast
was:

1) impossible to implement
2) insignificant
3) not really a beast

largely in that order.

Thanks for that comprehensive summary. However I don't fully agree: in
Ruby land it *is* a beast - although it's not in Eiffel land. Note:
context matters. :slight_smile:

Kind regards

    robert

Nick Woolley wrote:

No type inference, just a little extra runtime overhead (which could be switched off). And/or similar optional preconditions on method arguments?

The latter is available through ruby-contract. See http://ruby-contract.rubyforge.org/

That's about my most common error too (it ties with a parse error due
to a missing 'end' statement).

···

On 5/4/05, Jon A. Lambert <jlsysinc@alltel.net> wrote:

Ben Giddings wrote:
>
> Static typing tends to find typos and brainos in code before it's
> executed.
>

My most frequent Ruby error:
"NoMethodError: undefined method `foo' for nil:NilClass"

Not a typo, and arguably anything is a braino.

Fixed by asking oneself two questions
1) How in sam hill did that get set to nil?
2) Can method bar can return nil, if blah blah foozle wimble?

ZenTest?

PS: ZenTest comes, free of charge, with unit_diff, which is the spiffiest test running helper tool ever. It runs your actual/expected outputs through diff helping you spot the differences in massive structures. Or even small ones of one or two lines.

···

On 03 May 2005, at 14:22, Hal Fulton wrote:

Eric Hodel wrote:

On 03 May 2005, at 12:27, Ben Giddings wrote:

My open question is then: how do you find typos and brainos in seldom-run
code in a dynamic language like Ruby?

Yes, unit tests and ruby -w. I wrote the thing in the 'I Rock.' post between midnight and 4am. As you can imagine, writing code tired is fraught with crazy typos, but the unit tests found them all.
When I was almost done, I spent 15 minutes on a 1 character typo (rrc in one spot, rcc in another). I didn't have a unit test for that case.

Let me put in a plug for code coverage tools in general.
Combined with good unit tests, these are truly invaluable.

There's a good one out there, but I forget what it's called --
<admission>I don't use it much, I've only played with it.</admission>

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

It's also interesting to note that perl does a simpler (but surprisingly
effective) check if you enable warnings:

code:

#!/usr/bin/perl -w

$a = 1;

print $aa + 3;

output:

Name "main::a" used only once: possible typo at test.pl line 3.
Name "main::aa" used only once: possible typo at test.pl line 5.
Use of uninitialized value in addition (+) at test.pl line 5.
3

Joost.

···

On Wed, May 04, 2005 at 06:18:11AM +0900, John Carter wrote:

On Wed, 4 May 2005, SER wrote:

>>Once again, static typing reared its head on the mailing list, and
>once
>>again, the knights of the Ruby table slayed the beast...
>
>It would be more accurate to say that the Knights claimed the beast
>was:
>
>1) impossible to implement

http://rubyforge.org/snippet/detail.php?type=snippet&id=47

Description:
Searches through all defined symbols for near misses in namespace.
Hopefully some of those listed will be mispelling bugs.

Hal Fulton wrote:

Eric Hodel wrote:
>
>>
>> My open question is then: how do you find typos and brainos in

seldom-run

>> code in a dynamic language like Ruby?
>
>
> Yes, unit tests and ruby -w. I wrote the thing in the 'I Rock.'

post

> between midnight and 4am. As you can imagine, writing code tired

is

> fraught with crazy typos, but the unit tests found them all.
>
> When I was almost done, I spent 15 minutes on a 1 character typo

(rrc in

> one spot, rcc in another). I didn't have a unit test for that

case.

Let me put in a plug for code coverage tools in general.
Combined with good unit tests, these are truly invaluable.

There's a good one out there, but I forget what it's called --
<admission>I don't use it much, I've only played with it.</admission>

Hal

Are there any code coverage tools for Ruby?

-Charlie

···

> On 03 May 2005, at 12:27, Ben Giddings wrote:

Joe Van Dyk wrote:

Ben Giddings wrote:

Static typing tends to find typos and brainos in code before it's
executed.

My most frequent Ruby error:
"NoMethodError: undefined method `foo' for nil:NilClass"

Not a typo, and arguably anything is a braino.

Fixed by asking oneself two questions
1) How in sam hill did that get set to nil?
2) Can method bar can return nil, if blah blah foozle wimble?

That's about my most common error too (it ties with a parse error due
to a missing 'end' statement).

Yes. Most languages just shift this "boundary problem" into
different paradigms and terminology.

In Java, the "pointerless language," the most common runtime
error is "NULL pointer exception."

Or in Pascal, "Attempt to dereference nil pointer."

Or in C, "Segmentation fault."

Hal

···

On 5/4/05, Jon A. Lambert <jlsysinc@alltel.net> wrote:

Python to the rescue! See, when your language doesn't have end statements
you can't *possibly* have any of these types of problems, right? *grin*

Ok, but seriously. We have "ruby -w" and "ruby -c" and "unit tests".

So let's have an analogy. Someone writs the following bit of C code:

int do_something(int foo) {
  int i;

  if (foo > 500) {
    printf("Whoa, %d is too many times! I can't do that!", fo);
  }
  else {
    for (i=0; i<foo; i++) {
      printf("%d\n", i);
    }
  }
}

int main(int argc, char **argv) {
  do_something(argv[1]);
}

The typical approach of a C programmer would be to try to compile that, and
if they noticed no errors, they'd run it.

The compiler would immediately catch:
* do_something() is being called with an character pointer as an arg, but
is declared as accepting an integer
* The variable used in the printf line "fo" was never declared.

Once the programmer fixed those two issues, the code would do what the
author intended.

The equivalent Ruby code would be:

def do_something(foo)
  if foo > 500
    puts "Whoa, #{fo} is too many times! I can't do that!"
  else
    foo.times { |i| puts i }
  end
end

do_something(ARGV[0])

"ruby -c" would say this is perfect syntax, which it is. So that's kinda
useless for typos and brainos

"ruby -w" would run it, and it would barf with this message:

foo.rb:4:in `>': undefined method `>' for false:FalseClass (NoMethodError)
        from tmp/foo.rb:4:in `do_something'
        from tmp/foo.rb:11

The line number is useful, but the message sure isn't. It turns out that
string > number complains about FalseClass. I dunno why. Anyhow, it's
obvious there is *some* error, so presumably the programmer would remember
to 'to_i' the arg to do_something().

Then there's the typo in the "puts" line. Until the person happens to try
an arg greater than 500, they will have no indication the code has a flaw.

Now, would a unit test catch this? If the programmer was careful, maybe.
But maybe not.

So, what advice would you guys give to a C programmer who wants a Ruby tool
to look over lines of code he may rarely execute?

Ben

···

On Wednesday 04 May 2005 12:21, Joe Van Dyk wrote:

On 5/4/05, Jon A. Lambert <jlsysinc@alltel.net> wrote:
> Ben Giddings wrote:
> >
> > Static typing tends to find typos and brainos in code before it's
> > executed.
> >
>
> My most frequent Ruby error:
> "NoMethodError: undefined method `foo' for nil:NilClass"
>
> Not a typo, and arguably anything is a braino.
>
> Fixed by asking oneself two questions
> 1) How in sam hill did that get set to nil?
> 2) Can method bar can return nil, if blah blah foozle wimble?
>

That's about my most common error too (it ties with a parse error due
to a missing 'end' statement).

Eric Hodel wrote:

Let me put in a plug for code coverage tools in general.
Combined with good unit tests, these are truly invaluable.

There's a good one out there, but I forget what it's called --
<admission>I don't use it much, I've only played with it.</admission>

ZenTest?

PS: ZenTest comes, free of charge, with unit_diff, which is the spiffiest test running helper tool ever. It runs your actual/expected outputs through diff helping you spot the differences in massive structures. Or even small ones of one or two lines.

I don't know ZenTest. I was thinking of rcov or whatever
it is called. Was it Simon S who took that over??

Hal

···

On 03 May 2005, at 14:22, Hal Fulton wrote:

unit_test is awesome! Here's an example from an article I wrote
recently (to be published soon) -- first without unit_test, then the
same thing with unit_test.

$ ruby ts_long.rb
Loaded suite ts_long
Started
F
Finished in 0.02506 seconds.

1) Failure:
test_long(TestLong) [ts_long.rb:33]:
&lt;"If you use open source software, you're sure to brush up against
some of its rough edges. It may be a bug, or a place where the
documentation isn't clear (or doesn't exist), or maybe it's not
available in your language.\n\nIf you want to give back to the
community, you can work on any of these problems, even if you're not a
programmer.\n\nYou could write a bug report for the developers (and
submit a patch if you're a programmer).\n\nYou could write or extend
existing documentation.\n\nIf you're really up for a challenge, you
could even work on translating the application and/or
documentation.\n\nHowever you choose to get involved, you should start
by contacting the developers."&gt; expected but was
&lt;"If you use open source software, you're sure to brush up against
some of its rough edges. It may be a bug, or a place where the
documentation isn't clear (or doesn't exist), or maybe it's not
available in your language.\n\nIf you want to give back to the
community you can work on any of these problems, even if you're not a
programmer.\n\nYou could write a bug report for the developers (and
submit a patch if you're a programmer).\n\nYou could write or extend
existing documentation.\n\nIf you're really up for a challenge, you
could even work on translating the application and/or
documentation.\n\nHowever you choose to get involved, you should start
by contacting the developers."&gt;.

1 tests, 1 assertions, 1 failures, 0 errors
$ ruby ts_long.rb | unit_diff.rb
Loaded suite ts_long
Started
F
Finished in 0.017676 seconds.

1) Failure:
test_long(TestLong) [ts_long.rb:33]:
3c3
&lt; If you want to give back to the community, you can work on any of
these problems, even if you're not a programmer.

···

On 5/3/05, Eric Hodel <drbrain@segment7.net> wrote:

PS: ZenTest comes, free of charge, with unit_diff, which is the
spiffiest test running helper tool ever. It runs your actual/expected
outputs through diff helping you spot the differences in massive
structures. Or even small ones of one or two lines.

---
&gt; If you want to give back to the community you can work on any of
these problems, even if you're not a programmer.
1 tests, 1 assertions, 1 failures, 0 errors
$

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

--
thanks,
-pate
-------------------------
We are often unable to tell people what they need to know, because
they want to know something else, and would therefore only
misunderstand what we said
- the Raven (George MacDonald, Lilith)