Given a method like…
def myFun(val) puts(val+3) end
If I accidentally call it with the String “5″, I get a type error, which looks like this:
test.rb:2:in `+': can't convert Fixnum into String (TypeError) from test.rb:2:in `myFun' from test.rb:4
That’s fine by me, but now I’d like to give a more meaningful error message (“Hey, buddy, you meant to pass in something that can +3. Like a Vorpal Sword.”). At that point, though, I’m basically hand-rolling my own static typing, except without all the useful compile-time enforcement or automatic checking.
Note the corresponding Perl subroutine Does The Right Thing:
sub my_fun($) { print ($_[0] + 3) } my_fun("5")
And I don’t mind that. If you’re going to play it fast-and-loose with types, that really is fine by me. But Ruby gets on my nerves with the way it does not let me do typing when useful and yet eagerly punishes me for using the wrong types. If a language is going to give me the responsibility of handling types, give me the power to handle them well. If it’s going to take responsibility for handling types, then it can have the power. There are domains where each of these is an appropriate take on things. But giving me the responsibility while not giving me the power simply irks me.
19 Comments
ugghhhh… that’s exactly what I hate about the perl/vb/php school of “fast and loose” dynamic typing. The errors that come out of those automatic conversions are impossible to debug.
I think you’re missing the Zen of Duck Typing; you’re *not* hand-rolling your own type checking, you just specify the interface that an object needs to implement to be used in a function. My comments will often say “@foo is a string-like variable”, and python or ruby will make sure that if you pass in a non-string-like variable, the function fails.
That’s not hand-rolling my own static typing system, that’s using the duck typing system of the language, keeping flexibility into runtime instead of locking everything down tight at compile time.
I’m not sure I understood. Your problem is with Ruby or strong dynamically typed languages?
@bill. Isn’t “5″ a “string-like variable”? It’s in quotes, so it’s a string. But it could also be interpreted as the number 5. I’ve started using the term “type juggling” more than “duck typing” to indicate that actual juggling might be happening. The “if it walks like a duck and acts like a duck it’s a duck” mantra is fine, but the “acts like a duck” is precisely what’s in question here, no? Should “5″ be able to be cast at runtime to 5 without throwing errors? PHP would say yes, and apparently, above, Ruby says no. (Insert “computer says no” from Little Britain here)
It’s possible to not “lock things down tight at compile time”, yet still have sane checking (as my resurgent post shows). To that end, I have great hopes for some Duby-inspired revolution in our thinking about types.
As for the old chestnut of “the documentation handles it” — IF documentation was consistently high-quality enough to catch these kinds of issues, and IF the “-like variable” structure was vague enough to be sufficient, then I’d be fine with the basic take. But one glance at REXML will tell you that the documentation isn’t that high, and I’ve got battle wounds from Rails
has_manywhich returned a “list-like variable” yet don’t implement all of the list API. So calling that code was always kinda a crap-shoot, especially since you had to be really, really sure your unit test hit that case, or you’d never actually know in development.If a language is going to give me the responsibility of handling types, give me the power to handle them well. If it’s going to take responsibility for handling types, then it can have the power. There are domains where each of these is an appropriate take on things. But giving me the responsibility while not giving me the power simply irks me.
Your position scans like you prefer Visual Basic to typed languages.
There’s certainly a range on the typing scale, from strong static typing through strong dynamic typing through to weak dynamic typing, but you seem unusually attached to the extreme right end of that scale.
Do What I Mean Not What I Say isn’t a scalable way to build systems because of the unpredictability of the decisions made for you at every abstraction boundary between you calling with something at one end, and something getting done way over at the other end.
Still the “not scalable” canard is trotted out. I understand you’re stopping short of saying loosely-typed languages do not or can not scale, and are saying it’s just “not a scalable way to build”, but I’m not quite sure what that means. Loosely typed languages build large scale systems, and provide a way to do so quickly and efficiently, depending on the skill of the developers involved. Skill and experience have far more to do with “scalable ways” of building something than variable typing.
@Barry Kelly
Check other posts on this blog — lots of discussion on typing. I agree with you that Perl’s system is a really shady way to do very large systems. On the other hand, if what I’m searching for is a portable, extensible scripting tool (like the build for my Cornerstone language), that kind of typing is perfect. And it’s notable that PHP has this kind of typing, but it’s hard to say that WordPress isn’t a large system.
On the idea of where dynamic typing is and isn’t appropriate, see the conversation over on A Defense of Prototypes, or, Why Does Tom Christensen Hate Perl? If you haven’t seen it, you probably also want to check out My Frustrations with REXML: Ruby’s Standard Library for Reading/Writing XML and 7 Actually Useful Things You Didn’t Know Static Typing Could Do: An Introduction for the Dynamic Language Enthusiast.
@Robert, not just wordpress. Yahoo! uses PHP for some of their properties. Digg is (IIRC) all PHP. Flickr is PHP-based – or was at any rate. My info on that is a few years old, but I’ve no reason to think it’s changed (http://www.niallkennedy.com/blog/2004/10/flickr-architec.html – look! even a bit of Java in there!)
Well, it’s hardly a concern for folks doing TDD. I don’t have any problem with dynamic languages. I tend to see static typing as the exception rather than the desirable norm.
How does TDD solve the documentation problem? You still have to have good documentation and/or try the code out, see the failure message to get the type you need, and then apply the type conversion.
Insofar as it gets in the way, explicit static typing is certainly not a desirable norm. Insofar as it catches bugs and provides documentation, though, it is desirable (unless you have a problem with documentation or catching bugs?).
So the question is how to maximize the bug catching/documentation aspect while minimizing the “getting in the way” aspect.
@mgkimsal
Thanks for the comment and the other properties. I’m just not up on my PHP evangelism. :)
Sure thing. PHP is the bastard step-child of the programming world, yet there’s a number of pragmatic things it offers which give people reason to overlook its shortcomings in more traditional areas. The “edit, save, reload” processing time is next to nothing. Grails is starting to get in the same vicinity, but there’s still too many times when things change in a Grails app where you need to restart the server, or wait while numerous things change (and potentially break). Grails is a far step from “traditional” Java, and I understand why Java devs feel more productive doing web stuff with Grails (and possibly other JVM-based frameworks). Until something else offers the same speed on the “edit, save, reload” iteration cycle (perhaps Ruby comes close?) PHP will continue to be a part of many web developers’ toolkits, despite being loosely typed.
I’m not sure I agree that Perl “Does The Right Thing”. I actually had to run the program to see the output. Isn’t “53″ just as valid as “8″? Maybe it makes sense because you already know how Perl does the conversion.
In this example, I think that Ruby raising an error, “Dude, I can’t do that, I don’t know what you _mean_?”, is the more sane approach.
As an aside, Python would give you the same type of error.
You could use #to_i.
def myFun(val) puts(val.to_i + 3) end
As “postmodern” says: the Ruby way is that, if you want a “foo” then you ask for a “foo”. However, first I’ll stop and point out that you should have said:
puts(3+val)
…because running val#+ for a not-very-specific type could have a very unpredictable result: for example, where “5″ + “0″ is “50″ (string concatenation through the plus sign, which Ruby does), “5″ + 0 should be “50″ too (though see below), and that’s just a trivial not-an-object-in-the-usual-sense case. In a way, the fact that you got an error is an advantage of the lack of implicit type conversion because it’s highlighting this undefined behaviour, but of course languages like Perl get around that by having only explicitly different string concatenation and arithmetic operators.
There’s also the issue of how. In principle Ruby would automagically support your code if the conversion were unambiguous and foolproof (see String#to_str), but consider these cases:
“some text” + 5.0
5 + “1e23″
Yeah, the latter is a little contrived, but bear in mind that you’ve already got data of an unexpected format coming in. That string could convert to a number in a variety of ways:
>> “1e23″.to_i
=> 1
>> “1e23″.to_f
=> 1.0e+23
>> “1e23″.to_i(16)
=> 7715
>>
I was really thinking of the last one there. So, yes, basically if you want a variable to be cast to a particular type for the whole scope of your method you’d do something like “var = var.to_i” at the top.
Ruby will, however, happily convert on its own when it’s pretty clear what the results should be, eg. promoting int to float (double) as need be, similarly to how C does it.
@Curtis
In that 3 is an integer and not a string, “5″ + 3 makes sense to be 8, not 53. If it were “5″ + “3″, that’s a different story and you could debate it. Although I note Perl explicitly distinguishes between String concatenation (“.”) and addition (“+”), which is good. For one thing, addition is associative and reflexive, yet string concatenation is neither, so “+” is a really horrid symbol choice.
BTW, one of the ideas I’m kicking around for Cornerstone is automatically inheriting unit tests when you implement an interface/typeclass/functor/whatever. So the “+”-able type class would provide a unit test proving out reflexivity, associativity, and a “zero” concept with the associated rules. The “—”-able type class would provide unit tests proving out b – b = 0. Stuff like that. Not sure how I’d get it to work yet, but it’s something I do so commonly in Java that it’d be nice to bake it into the language in one way or another.
Also: I agree with you that erroring out is a more sane approach. But if that’s the approach it should be following, it should give me the ability to quickly give more immediate, meaningful errors. This is where optional typing looks very nice.
@postmodern
You’re totally missing the point of the post.
@J D
Yeah, it’s pretty common to encounter Ruby code that says something like “responds to #to_i and returns an integer”. At that point, though, aren’t you basically just specifying a type? Sure, it’s a type with one member — “#to_i” — but it’s still a type. Why not just ask for an integer? And, if you’re going to just ask for an integer, why not have an automatic check to make sure the thing passed in is an integer?
Even more so, I note that that kind of maneuver just destroyed the very advantage offered by duck typing in the first place. What if I really did want to pass in a vorpal sword, and let it get +3′ed and the result printed out? The “#to_i” approach undercuts that capability completely.
@Michael “5″ is of course a string-like variable (it’s a string!) but it annoys me when languages (like perl, php, and vb) will auto-cast it to a numeric value because they “do what I mean”.
@Robert I’m not defending REXML! That library is a particular turd by any standards (evidence).
@Bill Mill
The fact that such a turd is in Ruby’s standard library is exactly my point. Saying “don’t do that” or “the documentation covers that” is a very, very poor excuse for a bad type system. Sometimes people do things they shouldn’t, and sometimes the documentation is very poor. Sometimes doing things that way or adding external documentation (that is, documentation that isn’t code itself) is the more painful route. In that case, explicit typing is nice — it provides the inline documentation with a free code check to ensure your documentation isn’t out of sync with reality!
At the end of all of this, that’s my point. I’d be a much bigger fan of Ruby if it got off the fence, and either had more explicit typing or less strict types. It seems to be straddling a point that gives it the worst of both worlds.
2 Trackbacks
[...] in My Fundamental Issue with Ruby, I complained about Ruby’s types without the ability to work with/understand [...]
[...] a whole lifetime of ranting against it (cite, cite, cite, cite), I finally have to eat my words, come out, and say it: Ruby is the language of the [...]