Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is IMHO far more useful and interesting comparison than the original article; thanks for taking the time to write it.

I think the concrete examples really help show up the differences between the languages here. I might quibble with your initialisation in ruby (I'd expect it to be initialised with a string and hide the internal representation), but it is clear here why the Haskell type system might help you avoid issues with changing an interface and forgetting to change things which call it (though I can't say I've run into that a lot in Ruby, I can see where it might be useful, particularly with large groups collaborating or a large program).

I would argue that the compilation step is what is responsible for finding errors at compile time rather than runtime, but then the type system is perhaps required to enforce that.



I would argue that the compilation step is what is responsible for finding errors at compile time rather than runtime, but then the type system is perhaps required to enforce that.

It is. A Ruby (or Python, or JavaScript, ...) source code inspector cannot, in general, infer concrete types for variables. You're allowed to say (JS example)

    var x = "fred";
    x = 3;
and now, if you try to write a JavaScript inspector to infer the types so it could check that you used x correctly, it would not be able to.

Of course, there are cases where you can infer useful things about your code, but for most real-world JS code, it would produce a lot of false positives or false negatives, and wouldn't be very useful. (Think about monkeypatching...)


While this is a perfectly valid example, I think that it doesn't really show the crux of the issue, since the types involved (int or string) are simple and the temporal aspect of the type changing really doesn't come very often.

The bit where dynamic languages get really complicated is that they allow for you to use types that hadn't been taken into consideration when the language was designed. For example, in Python you can write code using duck typing and it will Just Work(TM) but if you want to do something equivalent in Haskell you would need tell GHC to use one of those weird language extensions just to have the program typecheck. The monkeypatching you mention goes more along this line, I think.


Sorry if my Ruby chops are weak - I've not used it for probably 5 or so years now :) (I do a lot of Python work though, so I'm familiar with dynamic type systems).

I kind of agree with your second sentiment - but I did want to point out that it's entirely possible to write a dynamically typed compiled language too.

It boils down to type systems: weak/strong, and dynamic/static. C, for instance, is weak/static: you define types, but you can basically pass around whatever you want to:

    #include<stdio.h>

    int main(int argc, char* argv[]) {
        int result = add(1, 2); 
        printf("%d", result);
        return 0;
    }

    int add(int a, char b[]){ 
        return a + b;
    }
The above will happily compile - and run. The method header for "add" says it takes an integer and a character array - essentially, a String in C (for people who actually use C: I've avoided pointers because they're confusing, and my C is even rustier than my Ruby).

Inside that method though, I do something entirely braindead: I add the integer and character array as if that were an operation that makes sense. Then I call "add" with two integers anyway, and let it do what it wants. It actually does return 3: rather than throwing a runtime error to say "hey, this is stupid, I should have a String here and you can't add a String to an integer!", it just trundles merrily along. A strong type system wouldn't let you compile or run this: it'd tell you off for trying to call a function with invalid arguments, then (hopefully) tell you off for using "+" in a nonsensical way.

FWIW, the following also works:

    #include<stdio.h>

    int main(int argc, char* argv[]) {
        int result;
        char aString[] = {'h', 'e', 'l', 'l', 'o'};
        result = add(1, aString);
        printf("%d", result);
        return 0;
    }

    int add(int a, char b[]){
        return a + b;
    }
The number returned and printed here is derived from the location in memory of the characters you've written. I think. Either way, it makes no sense (for most people, anyway - C hackers may have some use for such an operation).

At the other end of the spectrum, Python and Ruby are strong/dynamic: you can pass whatever types around you want, but the interpreter will crash if you try to do something the type system doesn't allow. You can't do 1 + "test": it makes no sense. It won't even try to run it and return something nonsensical, it just won't run.

All that said: I don't know if there are any strong/static languages without a compiler. I can't seem to think of an application of such a language: the best way to exploit a strong/static type system is to have it tell you, up-front, that you've made a mistake (and, of course, have your compiler optimize the pants off your code for runtime).

I also don't know of any languages which are compiled, but fully dynamically typed (Scala, maybe? Although it allows static typing too). Considering the benefits you can gain from type analysis during compilation, again, the concept seems a bad fit. Although (same as in the previous example), you could happily write a language that is!

So, yes, compiling the code is what enables you to perform those checks - but a strong type system is a part of compilation in its own right :).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: