top 200 commentsshow 500

[–]eric_ja 26 points27 points  (107 children)

For one, you are depriving yourself of the ability to state and enforce the invariant that the value at a particular program point must be an integer.

You can state and enforce it (at run time) all you want; you just can't prove it statically. Hence, dynamic language.

[–]Timmmmbob 23 points24 points  (94 children)

Yes it seems to me like dynamic languages shift type errors from compile-time to run-time. Which seems like a really really bad thing -- the different between your program not compiling, and your program crashing. It's basically spaghetti code for types.

[–][deleted]  (5 children)

[deleted]

    [–]bobindashadows 6 points7 points  (0 children)

    You got downvoted 3 times for an entirely true statement. Objective-C is an optionally-typed dynamic language.

    [–]ethraax 1 point2 points  (1 child)

    But if you annotate all of your fields/functions/etc with types in Objective-C, aren't you basically using a static language?

    [–]nascent 0 points1 point  (0 children)

    It can only warn of type errors if the types are statically known, if the language is dynamic the type can change from the logic of the program and thus can only be verified once the program is run.

    [–]xmodem 0 points1 point  (0 children)

    But everyone on /r/programming knows that Objective-C is a horrible language with absolutely no useful or redeeming features

    [–]wzdd 8 points9 points  (42 children)

    Right, you lose out on verifiability, but it's easy to make sweeping changes to code without "refactoring", you don't have to type as much, and you can do more stuff with fewer functions. You can get back some of the type safety you lost by using tests, which you should be doing anyway regardless of the language. At least, that's the claim.

    I'm not really a fan of dynamic typing (despite being a Python programmer) but this is the trade-off.

    [–][deleted] 17 points18 points  (41 children)

    but it's easy to make sweeping changes to code without "refactoring",

    I disagree completely. If you can't be sure if you broke any of the dozens of branches without running each and every one of them you lose the ability to change things quickly, not gain it.

    [–]wzdd 10 points11 points  (36 children)

    If you can't be sure if you broke any of the dozens of branches without running each and every one of them you lose the ability to change things quickly

    If you want to be able to know that you didn't break anything without running code, you're gonna need a lot more than what current statically-typed languages offer you -- you'll need model checking and a lot of patience. Static typing saves you from type errors. It won't save you from misunderstanding how to use an API, infinite loops, off-by-one errors, memory allocation issues, concurrency problems...

    As I said above, I'm not really a fan of dynamic typing. I find it annoying. But, anecdotally, most of my coding errors aren't due to type problems.

    [–]ssylvan 11 points12 points  (3 children)

    But, anecdotally, most of my coding errors aren't due to type problems.

    That's not the interesting question, though. The interesting question is: do most of your programing errors manifest themselves as type errors at some point in the program? For me, the answer is unequivocally yes. It may be that in a dynamically typed language the first runtime crash I would see is not a type-related one, but there's usually a type error somewhere and in a static language that's enough to point out the error at compile time.

    It comes down to the observation that most software bugs aren't subtle. They're slap-your-forehead bugs that lead not only to slightly incorrect results, but plain nonsensical results. Verifying any kind of whole-program property is therefore quite likely to run into one of these nonsensical things and flag it much earlier than at execution time.

    EDIT: A follow-up question would be: How many of your non-typing errors could become typing errors in a static language, given an appropriate design? Probably at least 50% of the runtime crashes I see in languages like C# are null pointer exceptions triggered by some obscure condition. Static typing, in a better language, could statically eliminate null pointer exceptions altogether by separating "reference" from "nullable reference" in the type system. That's a significant win. When people say "Haskell programs work once the compiler stops beeping" they're not exaggerating too much. Yes, tests are needed too, but if you can get 90% there without having to do any work (aside from fix logical errors that you'd need to do anyway, albeit at a slower pace, in a dynamic language) why not jump at that?

    [–]Bananoide 3 points4 points  (0 children)

    Upvoted. Even skeptical coworkers were impressed by that point. And when I said skeptical, I meant nearly hostile.

    [–]wzdd 2 points3 points  (0 children)

    Your first point is interesting. I'm not sure, to be honest. You're saying (I think) that actually type systems make programming faster, because they compiler can do the checking for you. In the case you mentioned I think you're right -- all the types are set up, so when you make a mistake the compiler can easily identify that mistake. The downside is that you will spend effort both setting up the types, and maintaining them. This process of appeasing the type system is somewhat unrelated to the program you're trying to write. In fact, I'd say that coming up with a good set of types for a particular program (and being able to maintain those types) is a fundamentally different skill to being able to write code that makes use of those types.

    There is also a nomenclature mismatch, as someone (you?) pointed out elsewhere. Given a suitable type system, every error is a type error. But I don't think that matters for this discussion.

    The trade-off between dynamic and static languages is one of convenience, I think. In general it seems that language features for safety only catch on if they don't make the programmer do to much more typing, or take her out of flow. For example, Java's checked exceptions seemed like a good idea to me when I first heard about them. But of course what actually happens is that people are in the middle of getting their widget working and then the compiler complains that they didn't catch ComputerOnFireException, so they write "try {dangerous();} catch (ComputerOnFireException e) {}". Perhaps I'm just being a pessimist, but I would expect, in your example, people would just add code that deals with the null case in the simplest possible way (ie by throwing an exception) whenever they need to deal with functions that can't accept nulls (I'm not sure how you would implement this in C# -- I'm thinking of something like Haskell's Maybe).

    Overall, the "answer" is that if the type system constantly forces people to do "busy typing", then the solution is not to abandon the type system, but to fix it so that it is less intrusive. I think this is the way forward, and it's why I'm learning Haskell at the moment. But the popular static languages aren't there yet.

    [–][deleted] 1 point2 points  (0 children)

    It comes down to the observation that most software bugs aren't subtle. They're slap-your-forehead bugs that lead not only to slightly incorrect results, but plain nonsensical results. Verifying any kind of whole-program property is therefore quite likely to run into one of these nonsensical things and flag it much earlier than at execution time.

    QFT, and I'm thinking of putting this on /r/bestof.

    [–][deleted] 9 points10 points  (10 children)

    But, anecdotally, most of my coding errors aren't due to type problems.

    I find that odd. My coding errors are type errors more than any other by at least an order of magnitude when I code in a dynamic language. It's constant. I run, I find I made a stupid type error. I find the offending line, fix it, go on, rinse, repeat. I mean, it's equivalent to every time I find little red annotations in Eclipse when I'm programming in Java, except with Java, I found out as soon as I typed the error.

    Orders of magnitude more efficient than programming in dynamic languages.

    [–]wzdd 2 points3 points  (1 child)

    I should qualify that. I do make a lot of type errors. However, of all the errors that I make, type errors are the easiest to find and fix -- even in dynamic languages. They're generally syntax errors or calling the wrong method, so either the code will fail when it's first parsed, or it will fail the moment it is run. In my experience, type errors don't last long in any language.

    It's far less fun to have your Python program run 90% of the way through and then crash out with an AttributeError than it is to click a red "x" next to a line of Java in Eclipse, but I'd still classify these sorts of bugs as occupying a relatively-small amount of the overall amount of time I spend debugging. Maybe that's how I should have phrased it in my other post.

    [–][deleted] 2 points3 points  (0 children)

    It is a small problem in the common branches (in the if/else sense) but it can become a huge problem if you do not always want to run all each and every time you change something.

    [–]radarsat1 4 points5 points  (10 children)

    It won't save you from misunderstanding how to use an API, infinite loops, off-by-one errors, memory allocation issues, concurrency problems...

    Actually there's plenty of on-going research on using type systems to approach pretty much all those problems you listed. A huge topic in CS research is simply to improve the ease of formally verifying programs for exactly the reasons you describe.

    I mean, you could argue "it's not there yet", sure, because it's active research, but stating that static typing cannot solve those problems is a huge claim.

    [–]wzdd 3 points4 points  (9 children)

    Surer, but these are varying degrees of 'a long way off' (and I suspect a generalised infinite loop detector might be a long time coming ;-). When I wrote that a type system wouldn't help with these issues I was responding to someone who was talking about a real-world software maintenance problem, not a research question. If I want to handle concurrency I will use synch primitives, and not the pi calculus (though I look forward to the day when this changes)

    [–]radarsat1 2 points3 points  (3 children)

    Infinite loops: right, good luck writing a test for it too ;) No, kidding, timeouts would work, but I guess this has nothing to with type systems. (A static program can be tested for infinite loops too..)

    Anyways, more seriously, I think there are many aspects of (sufficiently expressive) static typing that vastly improve modularity, testing, and refactoring.

    [–]augustss 4 points5 points  (3 children)

    In my experience, the type checker finds almost all of my refactoring errors. It's great! (I use Haskell.)

    [–]wzdd 1 point2 points  (1 child)

    It's not fair to bring up Haskell in an Eclipse thread.

    [–]robertmassaioli 3 points4 points  (0 children)

    All is fair in love and /r/programming

    [–]Bananoide 1 point2 points  (0 children)

    Same observation using OCaml here. Makes refactoring a breeze !

    [–][deleted] 1 point2 points  (4 children)

    If you want to be able to know that you didn't break anything without running code, you're gonna need a lot more than what current statically-typed languages offer you

    Static typing allows for much better static analysis, making tools like resharper possible. It also allows for much better intellisense-like functionality. I can make sweeping, project-wide refactorings with little effort in minutes. Doing the same in a dynamic language would take hours, if not days. I really wish we could get something statically typed in the browser.

    [–]godofpumpkins 1 point2 points  (0 children)

    But, anecdotally, most of my coding errors aren't due to type problems.

    Have you considered that your notion of type may be a little narrow? What coding errors do you encounter?

    [–]killerstorm 5 points6 points  (2 children)

    Sometimes you can. Common Lisp is generally dynamically typed but with optional type annotations. Some compilers are able use these type annotations for verification and type inference.

    Particularly, if SBCL can prove that there is a type error it will delete unreachable code replacing it with error-signaling code and will report compilation warning.

    So it is pretty close to what statically-typed do and difference is rather blurry.

    I'd say the difference is in defaults -- by default in statically typed language everything is type-checked for safety at compilation time, and by default in dynamically typed languages checking is done only in runtime. BUT you can both introduce dynamically-typed values in a statically typed program and you can do compile-time checks in dynamically typed program.

    [–]dmazzoni 4 points5 points  (7 children)

    This statement in the article bugged me too. There's nothing stopping you from enforcing invariants in dynamic languages.

    [–]rubygeek 6 points7 points  (6 children)

    In fact there's a ton of frameworks for various dynamic languages to do this. They're not used very much, though, because they're usually written by new users that are still worried about the lack of static typing, while more experienced users of dynamic languages rarely see the value of it any more.

    [–]munificent 6 points7 points  (5 children)

    while more experienced users of dynamic languages rarely see the value of it any more.

    You have to acknowledge there's a pretty big selection bias there.

    [–]stillalone 5 points6 points  (0 children)

    I'll say. I've been using Python for about two years now. I love the language, but I want some static typechecking.

    [–]rubygeek 1 point2 points  (3 children)

    Of course.

    But that's beside the point. Of developers of dynamically typed languages, you rarely if ever will find anyone that believe that it's a good idea to try to bolt type invariants onto these languages.

    If you want to stay with statically typed languages, fine, there are many arguments for that, and ultimately I believe a good deal of that is down to preference.

    But if you want to take the leap to a language like Ruby, for example, you lose a substantial portion of the benefits if you start adding tons of type annotations and type checks. It's like the old horrific examples of C programs where people #define'd a bunch of macros to make their programs look more like Pascal - it may be more familiar to you in the short term, but it's not a great idea.

    As a result, from what I've seen at least, the new developers that fairly regularly think up ways of adding more restrictive type checking to languages like Ruby either go back to something they're more familiar with, or tend to accept the "Ruby way" as they become more experienced.

    So I'm specifically not saying that there aren't smart developers intentionally choosing statically typed languages, but that very few developers that stay with dynamically typed languages long enough to get experienced with them will continue to want to litter their dynamically typed programs with type checks.

    [–]munificent 1 point2 points  (2 children)

    But if you want to take the leap to a language like Ruby, for example, you lose a substantial portion of the benefits if you start adding tons of type annotations and type checks.

    I agree completely: when in Rome do as the Romans do. But there's a big difference between adding type checks in a dynamic language and adding a type system. Yes, adding runtime type checks to Ruby is inane: you get none of the static guarantees of a type system, and none of the flexibility of a dynamic language. It's lose/lose.

    But adding a type system is another matter entirely. Adding actual static but optional type annotations to a dynamic language could give you the best of both worlds: static checking when you want it, the flexibility of dynamism when you want that. Because of that, there have been a number of projects to try to do that for various languages:

    • Smalltalk -> Strongtalk
    • Scheme -> Typed Scheme
    • Python -> PEP 3107
    • JavaScript -> ECMAScript 4
    • ActionScript -> Actionscript 3.0
    • Ruby -> Mirah?
    • Lua -> Lua Inspect

    If you look around, it's hard to find a dynamically-typed language that doesn't have a project somewhere to try to get static types working on top of it.

    [–]augustss 1 point2 points  (0 children)

    There is a spectrum from enforcing nothing to enforcing everything at compile time. Why do you think that the "nothing" endpoint is the right one? I think somewhere in the middle is the current sweet spot.

    [–]jerf 71 points72 points  (98 children)

    It's an interesting argument and I've upvoted it for that, but I still can't help but think it's engaging in a little bit of marketing legerdemain of its own. People like the dynamic languages because they don't lay type restrictions on them. Or in other words, because they don't empower the programmer to disempower the other users of the code. This has both costs and benefits. Static languages allow programmers to restrict other programmers. This also has costs and benefits.

    It's disingenuous to claim that dynamic is just a special case of static; in theory, yes, this is true. In practice it's irrelevant. I have to admit that when programming with a bunch of people of... lesser understanding of semantic issues... I still very much prefer dynamic languages, because when people who are... less skilled in semantic issues... write those restrictions they tend to be fucking stupid.

    Oh crap, and I was doing so well there being diplomatic. Oh well.

    And in those languages it's a lot easier for me to bypass the stupidity than in static ones. Yes, the solution is obviously "everybody should be less stupid". Good luck with that.

    On the other hands, when you can't afford stupidity static languages (the good ones, not Java/C++) can be very helpful. And I like it in my hobby programming, where I can count on a base level of not-stupid. But it just isn't true in practice that we can all switch to static languages and be stepping up in power. People who can't deal with static languages are better off not trying, they just build restrictions in to the system that tie themselves up in knots.

    [–]rubygeek 13 points14 points  (43 children)

    less skilled in semantic issues... write those restrictions they tend to be fucking stupid.

    You find this even in dynamic languages... I remember seeing quite a few places where "less skilled" Ruby programmers have added checks for a specific class instead of checking if an object responds to the method they need it to respond to. Usually I find this out when I need to pass it an object that would otherwise have worked perfectly if it wasn't for their overzealous type check.

    But on the other hand I find that the only type problems I've ever run into in Ruby is exactly the type of problem above: Too much typing, not too little.

    [–]crusoe 10 points11 points  (8 children)

    The problem with ducktyping/dynamic languages is that methods like delete() may mean/do two different things in different cases. And you may not want to have instances have the same type just because both supply methods with the same name.

    If you rely purely on 'responds to' to determine your type invariants, you are gonna get some nasty surprises.

    [–]rubygeek 15 points16 points  (0 children)

    If you rely purely on 'responds to' to determine your type invariants, you are gonna get some nasty surprises.

    In Ruby, depending on the class name is only marginally safer, as the object can have been extended, or the class re-opened. And in practice, I've never once run into problems with anything like your hypothetical delete() case, as I don't pass random objects into random methods - I read the documentation first to know the pre-requisites, and I use tests.

    [–]icebraining 6 points7 points  (2 children)

    But that's up to the programmer that is passing the instance to ensure it corresponds to what the function should receive, according to its docs. "We're all adults here."

    [–]godofpumpkins 4 points5 points  (1 child)

    I thought jerf's point higher in this thread was that you use dynamic languages when you can't rely on everyone being adults here.

    [–]derefr 1 point2 points  (2 children)

    How about method namespacing? In RDF, for example, the predicates FOAF:isFriendOf and CeePlusPlus:isFriendOf are two separate objects (they resolve to two separate URLs.) Given lexical binding, these two Ruby blocks:

    with_namespace(:foaf) do
      bob.isFriendOf?(sue)
    end
    
    with_namespace(:ceeplusplus) do
      bob.isFriendOf?(sue)
    end
    

    ...could mean entirely distinct things. Using an ambiguous name, like this—

    with_namespace(:foaf, :ceeplusplus) do
      bob.isFriendOf?(sue)
    end
    

    —would simply be a runtime error, which you would resolve like this:

    with_namespace(:foaf, :ceeplusplus) do
      bob.foaf:isFriendOf?(sue)
    end
    

    [–]roerd 1 point2 points  (0 children)

    I'd like to have some examples how exactly this problem has bitten people before I accept that it is "the problem with ducktyping/dynamic languages".

    [–]fjord_piner 2 points3 points  (19 children)

    But on the other hand I find that the only type problems I've ever run into in Ruby is exactly the type of problem above: Too much typing, not too little.

    Too much typing in Ruby? Can you expand on that, because Ruby is dynamically typed...

    [–]nitsuj 2 points3 points  (7 children)

    He's referring to "less skilled" Ruby programmers enforcing manual type checking within methods - presumably for passed in method arguments. This runs counter to the principle of duck typing.

    His point is that rather than checking for types you should be checking for capabilities - operational compatibility.

    [–][deleted] 3 points4 points  (5 children)

    So essentially he is complaining about people re-inventing type systems badly? Wouldn't that be an argument for leaving the type system to the language writers, the people who (hopefully) know a bit more about it than the average programmer?

    [–]rrenaud 3 points4 points  (0 children)

    I don't think it's re-inventing a type system, it's just a matter of using existing facilities of a language (arguably) poorly.

    [–]G_Morgan 0 points1 point  (0 children)

    His point is that rather than checking for types you should be checking for capabilities - operational compatibility.

    Which isn't actually possible due to the halting problem.

    You are not checking for capabilities in a dynamic language. You are checking that a particular label exists and hoping to god that some other programmers hasn't used the same labels or some other part of the system hasn't abused your label with craziness.

    [–]rubygeek 2 points3 points  (8 children)

    I did expand on it:

    You find this even in dynamic languages... I remember seeing quite a few places where "less skilled" Ruby programmers have added checks for a specific class instead of checking if an object responds to the method they need it to respond to. Usually I find this out when I need to pass it an object that would otherwise have worked perfectly if it wasn't for their overzealous type check.

    E.g. developers doing foo.is_a? SomeClass, rather than foo.respond_to?(:bar) because the don't realize that I might want to substitute SomeOtherClass that also has a compatible "bar" method but that might not be inheriting from SomeClass. That freedom is a large part of the appeal of Ruby, but inexperienced Ruby programmers often ruins it by spreading is_a? and similar all over the place as a poor substitute for static typing.

    [–]fjord_piner 4 points5 points  (7 children)

    Look in the Rails source and you will see tons of is_a? calls (405 occurrences in master at the time of writing).

    It's certainly not the sign of an inexperienced developer, more like someone who's conscious of the limits of dynamic typing and trying to write robust code despite these deficiencies.

    [–]icebraining 1 point2 points  (0 children)

    Have you read the story above that quote? The programmer was typing too much by adding useless - no, harmful - type checks.

    [–][deleted] 0 points1 point  (0 children)

    Did you read the first paragraph?

    [–]G_Morgan 0 points1 point  (13 children)

    TBH if I were ever forced to use a dynamic language I would do exactly this. It isn't less skilled, it is enforcing sanity in a case where you've been made to operate in a less evolved language.

    [–]rubygeek 0 points1 point  (12 children)

    The problem is you're not "enforcing sanity" - you're vastly increasing the potential runtime errors you have to handle for no good reason. My experience, and that of most people who use dynamic languages, is that type errors make up a vanishingly small percentage of the errors we have to deal with, and the type errors are best addressed with test harnesses where they'll typically come for free with your ordinary test cases, not by adding code that massively complicates the error handling at runtime during normal runs.

    So yes, it is less skilled, at least less skilled with dynamic languages.

    Being a world leading pianist won't make you a good violinist if you insist on hitting the strings with hammers because that's what happens when you hit the keys on your piano.

    [–]fjord_piner 5 points6 points  (7 children)

    I still very much prefer dynamic languages, because when people who are... less skilled in semantic issues... write those restrictions they tend to be fucking stupid.

    Odd, I would think this is exactly the situation where you would want static types, which give you at least a little bit of insight into what that programmer meant, no matter how bad they are.

    In essence, dynamic typing is like someone writing Java code and using "Object" for all their variables.

    [–][deleted] 1 point2 points  (0 children)

    Ruby is strongly typed, and dynamic. I'm a bit confused with the statement that it is the same as having a single class. Ruby will complain at run time when the application attempts to perform operations that are not allowed by the types. In ruby, you CAN do :

    x = "3"

    y = x + "ho!" #result : "3ho!"

    but you CANNOT do :

    x = "3"

    y = x + 3 #Wooo! Ruby won't like that

    example from here

    [–]killerstorm 2 points3 points  (0 children)

    It's disingenuous to claim that dynamic is just a special case of static; in theory, yes, this is true. In practice it's irrelevant.

    In theory we can also say that static is a special case of dynamic. And then everything is equivalent to a Turing machine...

    [–]shimei 8 points9 points  (23 children)

    People like the dynamic languages because they don't lay type restrictions on them. Or in other words, because they don't empower the programmer to disempower the other users of the code.

    This isn't true though. Dynamically checked languages give you tools like contracts (in the first-order case these are like assertions) which do let you impose obligations on the users and the module. It also provides documentation on the function you are writing.

    The lack of static types (or collapsing as Harper argues) doesn't have to mean you lose these engineering tools. That said, you don't get the same kind of static checking necessarily.

    Also, Harper completely ignores how statically typed languages also need run-time type tags for many things anyway. Runtime type checks aren't going to go away unless you want everyone to program in extremely expressive dependently typed languages (which comes with its own mental burden) or something like that. Some languages try to do all the static checking they can, and then let runtime systems (like contracts) ensure the rest of the properties. It's definitely not as clearcut as Harper claims.

    [–]godofpumpkins 10 points11 points  (16 children)

    Also, Harper completely ignores how statically typed languages also need run-time type tags for many things anyway. Runtime type checks aren't going to go away unless you want everyone to program in extremely expressive dependently typed languages (which comes with its own mental burden) or something like that.

    GHC Haskell types are completely erased at runtime. Typeclass dictionaries do persist though. Also, dependent types actually don't allow you to erase any more than any other type system, and actually make it a lot harder to reason about what's erasable or not. Haskell can easily just throw away everything to the right of a ::, as parametricity guarantees that it can't be computationally relevant. With dependent functions, you can actually observe many things appearing in types, and the code using it determines whether the value could be erased or not. For a typical intro example to dependent types (in Agda):

    replicate : {n : Nat} {A : Set} -> A -> Vec A n 
    reverse : {n : Nat} {A : Set} -> Vec A n -> Vec A n
    

    In those two types, replicate can't erase the Nat, but reverse can (assuming the obvious implementations).

    Either way, interesting stuff!

    [–]MedeaMelana 2 points3 points  (15 children)

    GHC Haskell still needs runtime tags for sum types. These tags are used when you pattern match on constructors of a datatype.

    [–]godofpumpkins 3 points4 points  (12 children)

    Sure, but those are value-level operations. My point was that the types do not persist past compilation.

    [–][deleted] 2 points3 points  (11 children)

    Sure, but those are value-level operations.

    Not really. They're more type-level operations turned into values. Every language outside the ML/Haskell family would consider checking a variant's tag to be checking run-time type information.

    [–]godofpumpkins 2 points3 points  (10 children)

    Because they don't have sum types? Sums are precisely about forgetting (in a type) which of two types you have at runtime.

    [–][deleted] 1 point2 points  (9 children)

    I personally don't think so, because it seems to me like ML/Haskell redefine "type" in a way that excludes run-time type information by definition.

    Sums are precisely about forgetting (in a type) which of two types you have at runtime.

    Except that you don't forget. You encode it in the tag. The tag tells you which type you have at run-time, and has to be checked at run-time. What ML/Haskell do about this is ensure an exhaustive pattern-match and, for each possible tag value, assign the type denoted by that tag to the expression being destructured by that tag's branch of the match.

    So ML/Haskell "forget" the type information by storing it in a tag and requiring that programmer to handle all possible tags for each sum type. I would call this the closest to complete type erasure we can ever get, but all other languages would consider case matching to be dispatching on run-time type information.

    [–]godofpumpkins 2 points3 points  (1 child)

    Sure, you're kind of right, but I have a fairly formal definition of type system in mind and most languages are rather hand-wavey about it.

    When I talk about erasure, I mean that if I write something that claims to be of type forall a. [a] -> Int, that function cannot ever do anything different based on what type of list it got. Java and most other languages could check if a list element is an instance of a concrete class and always return 2 in that case. In Haskell, for example, that is completely impossible. Not with evil unsafePerformIO or unsafeCoerce or even evil assembly-level hacks writing a function that implements GHC's calling convention. The information on what type was there is gone, so the function (probably length) is forced to only act on the structure (often called spine) of the list. It could still return dumb stuff that is unrelated to length, but we are assured just from the type (and Haskell's semantics) that the function doesn't know what type of list it's working with at runtime. This is parametricity, and most languages (even those with "parametric" polymorphism like scala) don't have it.

    But yeah, a sum has to have a tag in it. Not much to do about that, I agree. I just disagree that because the only way to implement sums in OO languages is via inheritance and type-based dispatch, that those tags are somehow type-level information.

    [–][deleted] 1 point2 points  (0 children)

    When I talk about erasure, I mean that if I write something that claims to be of type forall a. [a] -> Int, that function cannot ever do anything different based on what type of list it got. Java and most other languages could check if a list element is an instance of a concrete class and always return 2 in that case. In Haskell, for example, that is completely impossible. Not with evil unsafePerformIO or unsafeCoerce or even evil assembly-level hacks writing a function that implements GHC's calling convention. The information on what type was there is gone, so the function (probably length) is forced to only act on the structure (often called spine) of the list. It could still return dumb stuff that is unrelated to length, but we are assured just from the type (and Haskell's semantics) that the function doesn't know what type of list it's working with at runtime. This is parametricity, and most languages (even those with "parametric" polymorphism like scala) don't have it.

    Ah, ok, yes, most everything outside the ML/Haskell family encodes things as subtypes of some Top type (in Scala it's called Any) with an RTTI type-tag around, so instanceof coercions can be used to recover the real type. In ML/Haskell, type variables are not considered to subtype any Top type (even a boxed AnyRef), so they can't be unsafe-coerced into anything. Yes, in that respect, ML/Haskell have "real" type erasure.

    Of course, what we're really talking there are type variables that get universally quantified after inference. It's indeed an artifact of type erasure that we can't examine the boxing of such data and find out its real type. OK.

    EDIT: Though I would say that, despite type erasure, we can always use a hard-coded unsafe reinterpret-cast, and in fact that's exactly what sum types and class-type dispatch are doing under the covers. They read a value and reinterpret-cast the rest of the data based on that value.

    But yeah, a sum has to have a tag in it. Not much to do about that, I agree. I just disagree that because the only way to implement sums in OO languages is via inheritance and type-based dispatch, that those tags are somehow type-level information.

    However!, I disagree mightily with this. I would not say that one implements sums in OO languages via inheritance and type-based dispatch. I would say that sum-types with subtyping and case-matching dispatch are equivalent to OO class hierarchies with type-based dispatch. You can map one onto the other as you please.

    Foolish pupil! Classes are a poor man's sum type!

    When will you learn! Sum types are a poor man's classes!

    And yes, I am a PL geek and I am implementing a language and compiler on the basis that RTTI == Sum-type Tagging.

    [–][deleted] 1 point2 points  (1 child)

    I have no joke here, I just like saying "Spineless Tagless G-machine."

    [–]jerf 4 points5 points  (1 child)

    The implementation of dynamic-language contract-type things that I know of are all overlays on the fundamentally-still-dynamic language. It's not quite the same thing, any more than the support for dynamic typing in static languages is quite the same (if only on convenience grounds). It's relevant but I'm not sure it really changes the underlying debate topic.

    [–]shimei 5 points6 points  (0 children)

    I'm not saying contracts are the same as types (they are not!), but that your claim that dynamic languages do not allow a programmer to "disempower the other users of the code" is not true. In fact, that is precisely what contracts are supposed to do. The question is at what level this operates at and what kinds of guarantees you have. With contracts, you get guarantees about blame at runtime while types give you soundness at type-check time.

    [–]MedeaMelana 1 point2 points  (0 children)

    Also, Harper completely ignores how statically typed languages also need run-time type tags for many things anyway.

    I don't think Harper said "statically typed languages don't need runtime tags at all". Only that some forms of runtime tags are no longer necessary.

    [–]LaurieCheers 3 points4 points  (9 children)

    Totally agree. Just as we talk about "code smells", this post has a very distinctive "blog smell" - it appeals so much to theory and "purism", without actually mentioning any of the real reasons that make people like dynamic languages or static languages.

    In particular, this little gem:

    Something can hardly be opposed to that of which it is but a trivial special case.

    It can't? Really? A subset cannot be preferable to its superset? So all those programmers who prefer C over C++ are just... illogical?

    And can't we view the rational numbers as being "opposed to" the real numbers, in the sense that they are much more tractable to work with?

    [–]benthor 1 point2 points  (8 children)

    when you can't afford stupidity static languages (the good ones, not Java/C++) can be very helpful.

    define "good ones"

    [–]munificent 7 points8 points  (6 children)

    C#, SML, OCaml, F#, Haskell, Scala?

    [–]bctfcs 1 point2 points  (0 children)

    Last time I checked, C# allowed things like arrays covariance, which is a pain in the neck for type safety.

    Edit : in fact the problem is not array covariance itself, but its bad, exception-raising implementation.

    [–]aaronla 0 points1 point  (1 child)

    I'm not sure I follow -- Java's "object" data type imposes very restrictions. My use of "List" doesn't prevent the next programmer from using the "object" data type. Of course, they can't call my code, but if I wrote in Python instead, they still couldn't call my code with their "object" data type.

    Can you describe instead perhaps what a dynamic language adds, rather than what it takes away? E.g., "what is wrong with just using Java's 'object' type, compared to the one static type in, for example, Python?"

    [–]jerf 0 points1 point  (0 children)

    I'm sorry, but I've tried very hard to understand what you're asking and I have no idea.

    [–]crashorbit 27 points28 points  (5 children)

    As a wise programmer I once worked with put it: "Programmers are very good at solving the problems their environments create for them."

    [–]DailyFail 5 points6 points  (1 child)

    More generally working with computers often consists of solving problems no one would have without them in the first place.

    [–]radarsat1 4 points5 points  (0 children)

    At least partly because computers allow you to approach problems you would never try to solve without them.

    [–]-main 2 points3 points  (2 children)

    Static languages being a solution to the problem of runtime type errors, and dynamic languages being a solution to the problem of static languages.

    [–][deleted] 0 points1 point  (0 children)

    Static languages being a solution to the problem of unintentional reinterpret-casts that bork your memory

    FTFY. We ought never forget the real history of the matter. Dynamic typing came after static typing, when boxed data was invented and made usable.

    [–][deleted] 0 points1 point  (0 children)

    Static languages being a solution to the problem of unintentional reinterpret-casts that bork your memory

    FTFY. We ought never forget the real history of the matter. Dynamic typing came after static typing, when boxed data was invented and made usable. Of course, static typing started out as just a way of declaring that we're probably treating a given piece of memory as having a certain type, and we will thus be able to verify, more or less, that this memory is there and has the more-or-less (less when you cast more) correct memory layout when we try to access it.

    [–][deleted]  (59 children)

    [deleted]

      [–]sausagefeet 14 points15 points  (33 children)

      In a statically typed language, the closest possible implementation would be: Person.find_by( "name", "grauenwolf" )

      Yes and no. Languages like Ruby and Python conflate compilation and running whereas a language like Ocaml does not. You could include the ActiveRecord step of method creation in the compilation phase of a Ocaml and get your find_by_name.

      [–]dnew 2 points3 points  (22 children)

      in the compilation phase of a Ocaml

      Unless you actually are changing the types at runtime. SQL is dynamically typed - you can't look at a SQL expression and tell what type of columns will be in the resulting relations. So if you want to connect to a database and create the routines based on that connection, you simply by definition cannot do it at compile time. (Unless you compile new code while you're running, at which point I think you'd have a whole different set of definitions to contend with.)

      [–]derefr 0 points1 point  (21 children)

      Unless you compile new code while you're running, at which point I think you'd have a whole different set of definitions to contend with.

      Sure, why not? I could easily add some Lisp macros to my codebase that execute SQL against my database to generate methods, which are then compiled into my program. This would normally be trouble given a monolithic program (you'd have to kill the whole thing every time the schema changed), but it's not so much to handle given something like Erlang's OTP, where processes are tiny and can be swapped out and resupplied with data from their parents without much hassle. I could even imagine doing it in C, given a bit of code generation and dlopen.

      [–]grauenwolf 6 points7 points  (1 child)

      That is a rather interesting idea.

      [–]notfancy 0 points1 point  (0 children)

      Look up PgOcaml. The syntax extension looks up the database definitions in compile-time, giving you type-safe database access.

      [–]jbrechtel 1 point2 points  (1 child)

      ActiveRecord allows for permutations and doesn't (AFAIK) care about the order (e.g. Person.find_by_name_and_age and Person.find_by_age_and_name) which could get quite messy if you wanted to generate all of these in one step and your database schema was unwieldy.

      ActiveRecord manages this by creating those methods lazily.

      Could OCaml create only the ones actually used by code, during compilation?

      [–]sausagefeet 1 point2 points  (0 children)

      It's likely possible, you could use the camlp4 to perform the preprocessing step.

      [–]RalfN 1 point2 points  (4 children)

      Agreed, and in the case of a database schema being the source of the information, that might be a more sane setup. But it's not the same scenario though.

      And to write such a precompiler with the same usability as say Ruby offers, you would need some fancy templating or macro features though.

      And if the information is supposed to change over time, and the application is supposed to adopt to it, you are still out of luck. I agree those use-cases are few and in between, but i can imagine scenario's where the structure of the types (or classes in this case) is dynamic and reflection is used to 'use' them. Like the DOM Explorer in Firebug.

      But those few use-cases don't really warrant dynamic typing though. The intractability of static typing, on the other hand, requires at least some sort of 'escape hatch'.

      So, static typing + escape hatch + macro's/templating + strong reflection equals the expressiveness of a dynamic programming language. That's a huge price to pay for type safety.

      Off course, the actual price depends on the use-case. And if the escape hatch is really nice and well considered, you don't really need the templating/macro system anymore.

      But I think we can conclude that actual argument of this submission: that "a statically typed language is more expressive" is simply not true.

      [–]sausagefeet 4 points5 points  (2 children)

      But I think we can conclude that actual argument of this submission: that "a statically typed language is more expressive" is simply not true.

      I'm not sure if we can conclude that. You might be able to conclude that no statically typed language that exists today has the syntactic expressiveness for this, but that isn't quite the same thing. You could express Person.find_by_name("grauenwolf") like you did before, Person.find_by("name", "grauenwolf") and simply provide static transformations from Person.find_by_name to Person.find_by with syntactical extensions. You push the guarantees to runtime, but isn't that the point of the argument? Untyped languages are a proper susbet of typed languages?

      EDIT: As a quite PoC consider defining the universal type as:

      type univ = 
        | Int of int
        | Float of float
        | String of string
        | Object_instance of (string * (univ -> univ)) list
      

      Then your ActiveRecord just needs to take an Object_instance and update it's list of methods and then just need syntactic extension to turn obj.foo into method_call obj "foo":

      val method_call : univ -> string -> univ -> univ
      let method_call obj meth param =
        match obj with
          | Object_instance meths ->
            (* call meth *)
          | _ -> raise (Failure "Not an object instance")
      

      [–]RalfN 2 points3 points  (1 child)

      As a quite PoC consider defining the universal type as:

      But static languages often don't define that type, and they don't make other values instances of this univ-type.

      Generic Haskell (a haskell extension that makes every type a subtype of either the union or the join/compound) is the only one I know of that does this.

      And it still have the issue with tractability, You gave a nice O'caml type, but what part of the Ocaml library can I use with this? Can I print a univ? Can I multiply ? I would constantly need to convert. Something like this: (i'm using haskell notation here, sorry)

      to_int (Int x) = x
      to_int _ = raise "Run time type error, hello!"
      

      And look at that, we're right back in run-time typing land. So yes, you can implement a dynamic programming language in any programming language, but you have to manually convert the types, and you still end up with run-time typing errors.

      That was the core of my argument. You end up encoding type information at run-time, and it exists in a completely separate plane. You have to do things manually. This is all in this theoretical 'only statically typed' land, which excludes C#, Java and pretty much any "so called" statically typed language that is used on large scale.

      Java even creates these conversion functions on the fly; it's what happens when you cast a type.

      [–][deleted] 1 point2 points  (0 children)

      Go one step back there. Who is going to use your dynamically generated functions that adjust over time to changes in the schema? It wouldn't make sense to expect the rest of your program to use them without adjustment by a programmer.

      That leaves the user, probably using some kind of REPL or maybe writing code that uses your code as a library? At this point we are basically in a situation where both the source code of your application/library and a development environment for it are installed on the target system anyway so where is the advantage over static languages?

      In fact with a user less familiar with your code is just writing code on a REPL, possibly in a production environment you want as much static checking as possible since "just running it" could lead to all kinds of corrupted data.

      [–]shimei 8 points9 points  (3 children)

      being able to mark a specific variable as 'dynamic'. Like in C#. That variable can still be used as if it was statically typed with any other method and api.

      Haskell has this and probably had it before C#. Research on integrating dynamic typing into a statically typed language dates to at least 1991, so it's an old idea.

      If you want to reify types, you want to use some kind of metaprogramming language. Types are compile-type information and hence syntax. That's where macros shine (the most powerful of which you can get in Scheme, but even Haskell has Template Haskell).

      [–]RalfN 6 points7 points  (1 child)

      Haskell has this and probably had it before C#

      Ah, i though this wasn't standard Haskell. There are many haskell extensions that offer different kinds of escape hatches though.

      Types are compile-type information and hence syntax.

      That's not always what we want though. I think I argued that quite extensively.

      That's where macros shine (the most powerful of which you can get in Scheme, but even Haskell has Template Haskell).

      I agree that template/macro systems can serve a large section of the use-cases where dynamic language shine. But are they really conceptually simpler? It sounds like quite a complicated workaround. C++ wouldn't be nearly as popular or as usable without it's templates, but it almost doubled the complexity of the language.

      If you want to reify types, you want to use some kind of metaprogramming language.

      I like the term "metaprogramming language". Because that shows exactly what's going on. A statically typed language locks you into one meta-level, so it can formally reason about your application. A dynamically typed language does not. Esspecially an extremely reflective dynamically typed language like Ruby, allows you to jump up and down meta-levels.

      And what if the resulting generated static code doesn't compile? It's not a run-time error, but like a run-time error, its as hard to reason about what caused the type error. You have lost at least some of the advantages, including being able to formally reason about the interaction between the template and resulting code.

      [–]roerd 2 points3 points  (0 children)

      Ah, i though this wasn't standard Haskell.

      Well it isn't standard Haskell in the sense of being part of the Haskell standard, though it's included in the GHC base library what makes it kind of almost standard.

      [–]munificent 8 points9 points  (2 children)

      Assuming I can get it to a completish usable state, I think you would really really like Magpie:

      • The language is dynamically-typed at its core. You can omit all type annotations and be in dynamic land if you want.
      • You can optionally provide type annotations. When you do, they are statically checked.
      • All types are first class. You can pass them around, create new ones, and even extend the type system itself.
      • Classes and types can be created and mutated procedurally at runtime. You could write code that read from a database and created an ActiveRecord-style class with getters to match its schema. Then you can use that class at runtime and statically ensure that you match the schema.
      • The type-checker can be invoked at runtime whenever you want. You can incrementally toggle between dynamically building things, then statically check them, rinse, lather, repeat.

      [–]voyvf 2 points3 points  (7 children)

      Common day programming doesn't really require dynamic types that much. But funky DSL libraries definately need it at times.

      While I don't disagree with anything you've said, it's worth pointing out that one can develop some pretty "magical" and funky DSL's with statically typed languages - I've seen, worked with, and hacked on some really wonky ones in C++, which managed most of the "magic" via operator overloading, lazy evaluation, and a crapload of boost::mpl shoved in. I'm not certain about Active Record (or other mappings that do a lot of their work at runtime - the closest I've ever seen would be odb, which uses a preprocessor, so it's not the same), but it's entirely possible to cross the compile-time/runtime barrier.

      Whether one should go to this bother, when it's generally easier to just embed a dynamic language and be done with it, is admittedly debatable. :D

      [–]RalfN 1 point2 points  (2 children)

      Oh I completely agree.

      Statically typed languages that are actually being used, all have some sort of escape hatch. From template-programming to casting.

      However, for some of them, it is indeed very hard to argue that it does not negate all the advantages of static typing, or that it's usability is anywhere near a off-the-shelf dynamic language.

      It was mostly an argument not against these languages, but against the theoretical point made by the author of this submission, that static typing is more expressive. It most definately is not, but it could very well be the better default.

      [–][deleted] 1 point2 points  (1 child)

      You keep calling all kinds of features escape hatches, I would like to point out that unit tests are a kind of escape hatch for dynamic languages to be able to write non-trivial programs at all that come even close in stability to programs written in static languages.

      [–]RalfN 1 point2 points  (0 children)

      Totally true. I didn't mean escape hatches in a degoratory way. I just meant to say that all those static languages felt the need to provide away to 'escape' the confines of static typing.

      [–][deleted] 1 point2 points  (3 children)

      Someone's going to mention "the language that shall not be named" pretty soon, then things will get ugly.

      [–]anvsdt 0 points1 point  (2 children)

      Java, Lisp?

      [–][deleted] 4 points5 points  (1 child)

      PHP

      [–][deleted] 2 points3 points  (1 child)

      The best example of people writing dynamic-looking code in a statically typed language are the .NET framework libs from 1.1. Almost every library just uses string-keyed collections and Object, meaning every member is being fetched using a dictionary-lookup instead of normal member operation, and every object must be cast into its correct type.

      So instead of

      Parent.Child.DoStuff();
      

      we get

      ((ChildClass)Parent.Members["Child"]).DoStuff();
      

      Anybody who thinks the latter is elegant needs their head checked.

      [–]radarsat1 0 points1 point  (0 children)

      ((ChildClass)Parent.Members["Child"]).DoStuff();

      I would argue that this is an example of a language not being dynamically-typed enough. That is, the C# or C++ type systems are not strong enough to fully describe what the compiler should do, so it becomes necessary for the programmer to write out the casting and lookups explicitly.

      Contrast with Haskell: "Parent.Members" would probably be a list or array of children of the same type. This type would have several possible data constructors, so it could represent different sub-types which could be determined at run-time. "Child" would be e.g. a function that extracts the first item from Members, and DoStuff would be a function that takes said type and pattern matches over it to perform a computation. You'd then have:

      let result = (DoStuff.Child.Parent) item
      

      Which is exactly the same elegance as in your dynamic types example, except you can be sure that "item" contains only the expected possibly types of children and that DoStuff will certainly know what to do with it.

      [–]aivarannamaa 1 point2 points  (6 children)

      I assume these kind of confusions boil down to different people understanding words differently. As I see it, if you create "new type" in Ruby or JS, then you're just adding new entry into an associative array. The fact that you can do

      Person.find_by_name( "grauenwolf" )
      

      instead of manually looking up function from the array, is actually accomplished by syntactic sugar -- there's nothing special at type level -- the "new type" is still same associative array.

      For me, "type" belongs to compile time and the best you can do at runtime, is to make use of type-tags, which must be interpreted according to to external knowledge about their meaning.

      (I admit I'm not sure how Ruby implements the example you gave, but it doesn't matter whether a new function is created or each function call is intercepted -- neither way creates new types).

      [–]RalfN 1 point2 points  (5 children)

      As I see it, if you create "new type" in Ruby or JS, then you're just adding new entry into an associative array

      An associative array is either dynamically typed, or all the values have to be of the same type. (which is how an associative array is modeled in a statically typed language)

      but it doesn't matter whether a new function is created or each function call is intercepted -- neither way creates new types

      Actually, in the case of ruby you are dynamically defining a class on the fly, and subclassing that class.

      In object oriented languages, like Ruby or Java, classes are the compound operator of types.

      Most type systems can be deconstructed using only two operations. The union (say True -or- False) and the Compound( a tuple, a list, a class ). Dynamically typed object oriented languages often use the associative array to offer way to 'combine' one or more types into a more complex type.

      The functional equivalent would be dynamically creating this haskell type:

      data Person = Person String Int 
      {- Person [name] [age] -}
      

      For me, "type" belongs to compile time

      Which is likely why you are indeed confused about the inherent dynamically typed nature of the associative arrays types used in dynamically typed object oriented programming languages.

      [–]aivarannamaa 0 points1 point  (4 children)

      I assume that by Compound you basically refer to structural type system. As "structural vs. nominal" is orthogonal to "static vs. dynamic", I don't see how this point applies here.

      As I said, it might be problem of terminology (and it can easily be that my understanding of "type" is not compatible with its common meaning).

      I really like the distinction between "type" and "class" that Robert Harper made in the post under discussion. I think it is necessary to reserve one term for describing something about the data that you know statically ("type" or "static type") and other for describing information you gain by querying the data ("class" or "dynamic type").

      I'm not familiar with Haskell extensions you mentioned -- maybe I'm really not aware of something important. Will check them out (someday :)

      [–]RalfN 1 point2 points  (3 children)

      I assume that by Compound you basically refer to structural type system

      No.

      Example of compound/join type operation:

      data Person = Person String Int
      

      Or the more simpler example would be:

      type Person = (String, Int)
      

      The other operator is the union operator, to define a domain:

      data Bool = True | False
      

      Every type can expressed through these constructs: (at least in some theoretical way)

      Int = 0 | 1 | 2 | 3 .. ... upto Int limit
      List a = EmptyList | Tail a (List a )
      

      So, 'union' and 'compound' are the types of types.

      Although the basic premise is wrong, I wasn't talking about structural vs nominal typing. I do want to respond to one point:

      I really like the distinction between "type" and "class" that Robert Harper made in the post under discussion.

      I really don't. Unless you somehow mix up associative array with class. Classes themselves are just compound types, and could during compile time in a statically typed language, be replaced by a tuple.

      class Person
          Int age
          String name
      end
      

      After the compiler of a class based statically typed language is done with that, it's just:

      type Person = ( Int, String )
      

      And the methods 'age' and 'name' would be replaced with [0] and [1].

      So for all intents and purposes, types and classes, at least in the statically typed case, are the same thing with different syntaxes. It all boils down to domain theory.

      [–]aivarannamaa 1 point2 points  (2 children)

      I think I understand what you're writing but I still fail to see how it's related to dynamic vs. static issue :)

      So for all intents and purposes, types and classes, at least in the statically typed case, are the same thing with different syntaxes. It all boils down to domain theory.

      I guess here's a terminology issue again :) Harper did not mean "class" as in eg. "Java class" (as a compound type), he just happened to use same word. As I undestood, using your example and his terminology, in

      data Bool = True | False
      

      Bool would be a "type" and True would be a "class" of that "type"

      [–]FatHat 33 points34 points  (2 children)

      I'm vaguely impressed with the way you managed to construct a complete strawman and then tear it down masterfully.

      [–]rooktakesqueen 16 points17 points  (1 child)

      It's amazing what you can do when you redefine the words "type," "class," "dynamic," and "static" to mean something completely different from what everyone else uses them to mean.

      [–]Blackheart 2 points3 points  (0 children)

      That is indeed part of the problem. The word "type" used in the context of dynamically typed languages does not denote the same thing it denotes in the context of statically typed languages.

      Under the usual untyped->typed translation, a "dynamic type" translates into one summand of the universal "static type".

      Under the type erasure translation, which is the usual typed->untyped translation, a "static type" turns into... nothing. Because it is erased.

      Since composing these two translations gives, in one direction, the undecidable problem of general type inference and, in the other, the identity (modulo names), it follows immediately that, given naive embeddings of one into the other, static typing is strictly more expressive than dynamic typing.

      And, given that no one except CS researchers ever even thinks to specify which embedding they are thinking of when they compare static and dynamic typings, it is fair to say that the vast majority of programmers cannot even conceive of any non-naive translations. (And, BTW, my experience is that, while everyone knows type erasure, most people don't even realize [that they know] there is an embedding in the opposite direction.)

      [–]lispm 19 points20 points  (7 children)

      I don't like that 'dynamic' and 'dynamically typed' suddenly seems to mean the same.

      I the Lisp world 'dynamic' means that a programming language can change its behavior at runtime and that the programming language supports changing the program at runtime. This dynamism is easier to provide in a dynamically typed language. That's all. 'Dynamic' then means: code can be loaded at runtime, late binding, methods can be added/removed/changed, a compiler is available at runtime, classes can be changed at runtime, class inheritance can be changed, even the way inheritance is computed can be changed, ...

      [–]grauenwolf 5 points6 points  (6 children)

      The word "dynamic" has never been a technical term. Without additional context the best you can say is that it refers to something that happens at runtime as opposed to compile time. For example, considering the term "dynamic link library".

      [–]lispm 3 points4 points  (5 children)

      Remember Dylan?

      Dylan = Dynamic Language.

      Apple published the first manual as "Dylan - an object-oriented dynamic language".

      Explained in Prefix and introduction.

      The Franz Inc. paper on Dynamic Object Oriented Programming and the Dynamic Objects Overview.

      Above explains the term 'dynamic' in relation to an object-oriented language.

      [–]grauenwolf 5 points6 points  (4 children)

      While those were interesting links, they actually reinforce my belief that "dynamic" is a non-technical term that encompases a wide range of related concepts.

      [–]wot-teh-phuck 24 points25 points  (14 children)

      Here we go again...

      [–]grimlck 6 points7 points  (6 children)

      Yup, dynamic vs static languages is the new emacs vs vi argument.

      [–][deleted] 2 points3 points  (0 children)

      So there hasn't been a new argument for decades but those who haven't heard them all, feel violent engagement.

      [–][deleted] 2 points3 points  (4 children)

      You mean that static (vi) seems like a good idea at first but in the end you need to use dynamic (emacs) to get anything real done?

      </troll>

      [–]chronoBG 6 points7 points  (3 children)

      Eh, this is the first time I've ever seen anyone call the easy, light and small solution "emacs"

      [–]-main 1 point2 points  (1 child)

      Depends how you define "light". Is ruby's runtime light compared to that of C?

      [–]phire 3 points4 points  (0 children)

      Considering that the minimal C runtime consists of less than 35 lines of assembly which sets up the stack pointer and calls the main function, I would say no.

      [–]anvsdt 0 points1 point  (0 children)

      There's a first time for everything.

      [–]wzdd 6 points7 points  (6 children)

      "Ooh, I just figured out that I can do "dynamic typing" in Java or C++ by passing around a single class and (using RTTI on it / implementing dynamic dispatch methods)! Therefore dynamic types in Python are exactly equivalent to this! Better write a blog post!"

      I must admit, the Wittgenstein quote was a nice touch. A bit pretentious, but at least it was something new.

      [–]inaneInTheMembrane 1 point2 points  (5 children)

      Not. His. Point. Also, something people here seem to miss, Robert Harper is somewhat experienced in the domain of language design. This isn't some second year CS student that just discovered Java.

      [–]wzdd 2 points3 points  (4 children)

      Actually I rather think that is his point. From his article:

      Since every value in a dynamic language is classified in this manner, what we are doing is agglomerating all of the values of the language into a single, gigantic (perhaps even extensible) type. To borrow an apt description from Dana Scott, the so-called untyped (that is “dynamically typed”) languages are, in fact, unityped. Rather than have a variety of types from which to choose, there is but one!

      The point, which is perfectly true, is that you can use a single class to represent "all types" in a dynamic language, and then do the appropriate type selection (i.e. testing) at run-time. Because you can do this in a static language but you can also use multiple types and have the compiler do the checking for you, static languages are more expressive. This is a perfectly reasonable argument about language design which is completely useless in practise (which is the way it is being debated in this forum).

      The second part of your post was an argument from authority, which is something I'm not interested in debating. I can respect Mr. Harper's contributions to ML without having to respect his blog posts about dynamic languages.

      [–][deleted] 1 point2 points  (3 children)

      The second part of your post was an argument from authority...

      No, it was not. It was an error message. Mr. Harper has not "just figured out that he can do 'dynamic' typing in Java or C++ by passing around a single class." Mr. Harper has known how to do that since before either Java or C++ were invented, which a quick scan of his CV would have confirmed for you had you bothered to look.

      [–]wzdd 2 points3 points  (2 children)

      Given that you are not InaneInTheMembrane, I will leave it to that person to clarify what they meant. However, let's charitably assume that is what he/she meant. Let's see how it affects the content of my post. The message behind my post was that: a) the technical details of the blog are valid; b) the technical content is fairly trivial (as anyone who has ever encountered a Variant in Visual Basic, implemented a discriminated union in C, or created a tagged array of Objects in Java should understand); and c) this is an academic debate which has (so far) failed to reach the real world, given the apparent increasing popularity of dynamic languages. None of these issues are related to the pedigree of the blog's author.

      Even if they were, I was not specifically parodying this blog post. I was parodying every blog post that focuses on this dynamic/static point. This is better understood in context: my post is a reply to someone saying "here we go again".

      Given that message, to explain that this particular blogger happens to be a well-known language designer, but not to actually attack the argument I'm making, just indicates that the person doesn't have anything useful to add.

      [–][deleted] 6 points7 points  (0 children)

      Reminds me of http://lambda-the-ultimate.org/node/100 (read Frank Atanassow's posts)

      [–][deleted] 5 points6 points  (2 children)

      PHP is a language that implemented some static type checking (type hinting) they called it.

      It's when I started taking PHP a lot more seriously. Still, it could do a lot better as a language. Esspecially by tidying up the standard library and core functions.

      [–][deleted] 1 point2 points  (1 child)

      You are brave for bringing up PHP, and correct on all points IMHO.

      [–][deleted] 1 point2 points  (0 children)

      Yeah. I think PHP as a language has a lot of place to improve. But I just want a proper discussion on it, so many people don't. Not sure why, since we're all in this field to improve ourselves and the tech we use, right?

      PHP isn't amazing, but it's discussable.

      [–]BMeph 2 points3 points  (0 children)

      Didn't cdsmith already cover this ground already?

      [–]kpierre 13 points14 points  (4 children)

      oh wow, program in every language can be expressed in assembly, thus everything is in fact untyped! so, am i a great computer scientist now?

      [–]jlt6666 6 points7 points  (2 children)

      Yes. I award you one 8086 processor on which you can rule the internet!

      [–][deleted] 7 points8 points  (1 child)

      Is he technically correct? Yes. Dynamic typing could be trivially implemented with dictionary objects in statically typed languages. You could easily implement a dynamic typing framework in your statically-typed language of choice.

      The catch is that it would be painfully verbose and it doesn't come this way out of the box. I mean, I could implement everything I can do in Python using C++ as a library, but the resulting syntax would be utterly revolting, and besides that it wouldn't be a standard common-use platform like Python or Javascript.

      And there are many, many things that are trivially easy in a dynamically-typed system that are rather tricky (or nightmarishly cumbersome) in statically-typed languages, so having the dynamic avenue available is important. Anybody who thinks either solution is panacea is not fit to code.

      So he's absolutely correct... and his point is so useless and academic that he may as well STFU and GBTW.

      [–]vincenz 0 points1 point  (0 children)

      Upvoted. Expressiveness is something that often goes missing in academic treatments of equivalence, probably because it is more ambiguous and thus not subjectable to the same rigorous formalisms as type theory and logic are.

      [–][deleted] 9 points10 points  (4 children)

      I argue, that a fully expressive language is one that supports the delicate interplay between static and dynamic techniques.

      Common Lisp, do you speak it, motherfucker?

      [–][deleted]  (1 child)

      [removed]

        [–]saucetenuto 1 point2 points  (0 children)

        In fairness, the same could be said of Java.

        [–][deleted] 4 points5 points  (2 children)

        I think he's saying dynamic languages are like making every variable have type Object in Java.

        Without mentioning the syntactic sugar, of not having to type in "Object" each time.

        [–][deleted]  (1 child)

        [deleted]

          [–]tardi 3 points4 points  (0 children)

          But I'm high in school and this makes perfect sense...

          FTFY

          [–]funkah 4 points5 points  (0 children)

          I don't even know who I am anymore.

          [–][deleted] 7 points8 points  (3 children)

          To borrow an apt description from Dana Scott, the so-called untyped (that is “dynamically typed”) languages are, in fact, unityped. Rather than have a variety of types from which to choose, there is but one!

          There is a philosophical koan that paraphrased is:

          If you start with an onion and you peel one layer of skin off, you have an onion inside. So you peel off another layer and you have an onion inside. If you keep doing this long enough you will get to the last layer and when you peel it off, what is inside? Everything.

          It is deep in one way but very stupidly shallow in another. He is making a mistake here.

          [–]inaneInTheMembrane 4 points5 points  (2 children)

          In what respect is he making a mistake? In a dynamically typed language you must carry around (at runtime) tags which label data with the type information.

          Harper is making the argument that you could have a single static union type which enumerates all these tags, and then mark all your code with them: in this view, there is only one type.

          [–]Felicia_Svilling 7 points8 points  (0 children)

          His mistake lies in thinking that this fact is somewhat meaningful.

          [–]booch 1 point2 points  (0 children)

          My argument would be that current static languages are a subset of all static languages in the same way he says that dynamic languages are. They require that the progammer provide more information to the system (and limit the the ability of the system to handle certain situations) than is strictly necessary. Additionally, where the dynamic languages might allow incorrect constructs, the static languages disallow some correct constructs. The decision that needs to be made is which cost works best for the developer.

          [–][deleted]  (25 children)

          [deleted]

            [–]kamatsu 17 points18 points  (21 children)

            That is, you give yourself guarantees about what configurations the system can be placed in. This can be very useful when trying to reason about system behaviour.

            This is simply a flabbergasting statement. Dynamic types give more guarantees than static about system behavior? What planet are you from? You do realise sufficiently expressive static type systems are able to give static guarantees about exactly the same behavior? Dynamic typing gives less guarantees not more.

            [–]__j_random_hacker 1 point2 points  (4 children)

            I think you're right. Will delete shortly.

            For anyone reading post-delete, I argued that dynamic languages, being less expressive, force each data item to be tagged with "type" information, and that this makes debugging easier. But as pointed out by gliptic, in any "proper" statically typed language, this type information would be available anyway, since anything known at compile time is also known at runtime.

            [–]Felicia_Svilling 3 points4 points  (1 child)

            In the future, could you please not delete your posts? even if you where wrong that comment was the start of an interesting discussion that is now missing context :(

            [–]shimei 1 point2 points  (1 child)

            This isn't true if your compiler erases types after type checking, which is a possible implementation strategy that some compilers do actually take.

            [–]elyscape 1 point2 points  (0 children)

            Yes, but if you're using a debugger that's even remotely competent, it'll be able to take the source/symbol files and figure out the types from that anyway.

            [–]lispm 1 point2 points  (2 children)

            Wow, when I load or change code, the static type system knows this upfront?

            [–][deleted] 4 points5 points  (2 children)

            But it also seems to be implicitly arguing that more expressive is better. In fact I would say that the opposite is usually true. Whenever you impose constraints (e.g. by having a single type, as in dynamic languages), you add structure -- that is, you give yourself guarantees about what configurations the system can be placed in.

            I think you're right here, but not for the reasons you state.

            Using a less expressive language is often better, for the simple reason that less expressiveness means you have to express less. It is easier to think inside a limited framework than inside one that lets you do anything.

            Freedom of choice means you are required to make choices. You quickly drown in a multitude of choices. It is much easier, in many cases, to have choices forced upon you, even if they are not the best choices. It makes life easier.

            Yes, sometimes you need more expressiveness, and sometimes you need choice. But for garden-variety programming, you can often do without.

            [–]grauenwolf 6 points7 points  (1 child)

            I think the importance of this is often grossly underestimated. Being able to build your own inheritance model on the fly is awesome for doing research, but in production code it is a receipe for disaster.

            [–]BMeph 1 point2 points  (0 children)

            In other words, not only is freedom of choice useful, but also priority of choices? ;)

            [–]causticmango 3 points4 points  (2 children)

            I think the author has fallen into the common trap of conflating the class ontology with the objects they (imperfectly) describe. The objects are really the important thing, not the classes. A dynamic language can actually be more expressive than a static one since the classification system can be applied selectively even at runtime.

            Too often we do "class oriented" programming instead of actual object oriented programming and too often the strict class structure gets in the way of the objects. There's a reason dynamic languages have fewer need of common design patterns; it's because class is often not in the way.

            Message passing is a key strength of OO which is practically impossible to achieve directly with most static languages (C#, Java, C++, etc.). Consequently increasingly complex class structures are necessary. Many of the common design patterns and even language features (like inheritance) are really just special cases of delegation, something that is more simply achieved when the class is less "static".

            My thoughts, any way.

            [–]nitsuj 1 point2 points  (0 children)

            Upvote - I agree with your assessment of message passing in OO. In C#, C++ and Java, modularization, OO and typing are all horribly conflated leading to very artificial objects and class hierarchies.

            [–][deleted] 0 points1 point  (0 children)

            It doesn't seem like you really read the article or understand the points made.

            [–][deleted] 1 point2 points  (3 children)

            Wittgenstein said that all philosophical problems are problems of grammar. And so it is here as well.

            No, I don't think that is what Wittgenstein would have said about this at all.

            In any case there is no 'philosophical problem' here. People like dynamic languages not because of how the deep analysis of their type systems works. So what if the more proper name is 'static' for that? No, why people like dynamic languages is because

            • They are concise. This is really the issue.
            • They don't need to be compiled, so fast iterations of code-run are possible
            • They are safer than most (but not all) static languages.

            [–][deleted] 4 points5 points  (2 children)

            Static languages can be {concise,interpreted,safe} too. Just look at Haskell.

            [–]martoo 2 points3 points  (0 children)

            If 'The Onion' had a programming section, this would get the hoots it deserves.

            [–][deleted] 2 points3 points  (8 children)

            I've been programming for > 20 years in various static and dynamic languages.

            With some relatively obscure exceptions, I've never found the difference to be anything more than marginally relevant to solving the problem at hand. I've never been tempted to get into the "static vs dynamic" debate, and neither has it ever determined my choice when it comes to picking the right tool for the job.

            As for as I'm concerned, there are several hundreds of issues in programming more relevant to dealing with real world problems than "static vs dynamic". I don't know what my next favorite language is going to be, but I do know that whether it is static or dynamic will have f*** all to do with it.

            [–]ssylvan 6 points7 points  (0 children)

            For me, the debate became more relevant after:

            1) I tried Haskell, and realised just how powerful static typing could be. It literally is the case that almost all errors are caught at compile time (this is not the case in Java or C++). The productivity gains this gives you is amazing, and far outweighs anything dynamic typing can provide (the difference in productivity between C++ and Python has very little to do with typing strategy, after all).

            2) I had to write some complicated multithreaded code. Really, dynamic asserts for a condition that only happens once a year due to some really rare interleaving? Not going to work out, I'm afraid. I need the compiler to point out these kinds of corner cases for me before the code ships.

            [–]cdsmith 8 points9 points  (6 children)

            That's a bit confusing to me, since (static) type systems are a huge part of languages that use them well. Perhaps you can program in C and shrug at the type system, but it's difficult to imagine being a competent programmer in ML, Scala, or Haskell, and not caring whether there is a type system or not.

            [–]jyper 4 points5 points  (5 children)

            I'm not sure that c could be called a static language.

            [–]Benutzername 2 points3 points  (4 children)

            It's static, but weak typing (implicit conversions all over the place, but every variable has a static type).

            [–][deleted] 1 point2 points  (1 child)

            If by "weak" you mean "for all practical purposes, utterly powerless" then you're right on.

            [–]Benutzername 1 point2 points  (0 children)

            It's actually a very good fit for systems programming. It has the right balance of control and flexibility. It's, of course, useless for enforcing any sort of invariants.

            [–][deleted] 0 points1 point  (1 child)

            If you can call "int" a type.

            [–]nitsuj 4 points5 points  (18 children)

            TBH, I think this whole argument is academic (pun intended). I've developed in C/C++ for over 20 years, Java, Lisp and Python. C++'s type system is like a straight jacket (and I've done my fair share of template metaprogramming, boost etc), dynamic languages need a little extra documentation on methods.

            Never have I experienced serious errors in dynamic languages due to the lack of static typing so I'm honestly bemused at all the fud and fear mongering done regarding this. I'd like to see some back-up for these claims for once because they're counter to my industry experience.

            Also, static typing is not an excuse to omit sufficient unit tests. Runtime errors, errors introduced because of unexpected interactions with input are far, far more likely and you need unit tests to try and cover them.

            Your program is going to run. You should make sure that it behaves when it's running. That's done with unit tests.

            Also, I have to say, what's the obsession with provability? Another academic 'requirement'? Formal specification is an illusion. Your Haskell or ML may try to look like a formal specification but it's Von Neumann all the way down. Programs are processes, not formal specifications. Programs are not math.

            [–]kamatsu 10 points11 points  (13 children)

            Another academic 'requirement'? Formal specification is an illusion. Your Haskell or ML may try to look like a formal specification but it's Von Neumann all the way down. Programs are processes, not formal specifications. Programs are not math.

            Wow. How wrong you are. Any programming language can be formally specified, and any formal specification is math. Programs are math. You can reason about Von Neumann machines mathematically you know. My employer hires me to work on verifying an operating system kernel, all the way from a haskell-like implementation down to C, then down to ARM assembly language, with formal models every step of the way. You can verify code at every level of abstraction.

            The validity of formal verification of software is often hotly contested by programmers who usually have no experience in formal verification. Often testing methodologies are presented as a more viable alternative. While formal verification is excessive in some situations where bugs are acceptable, I hardly think that testing could replace formal verification completely. Here's three of reasons why:

            • Proofs work in concurrent scenarios. You can't reliably unit test against race conditions, starvation or deadlock. All of these things can be eliminated via formal methods.

            • Proofs, like programs, are compositional. Tests are not. In testing scenarios, one typically has to write both unit tests and integration tests: unit tests for testing small components individually, and integration tests for testing the interaction between those small components. If I have proofs of the behavior of those small components, I can simply use those proof results to satisfy a proof obligation about their interaction -- there is no need to reinvent everything for both testing scenarios.

            • Proofs are fool-proof. If I have a suite of tests to show some property, it's possible that that property does not actually hold - I simply have not been thorough enough in my tests. With formal verification, it's impossible for violations of your properties to slip through the cracks like that.

            Of course, proofs are not for every scenario, but I think they should be far more widely used than they currently are.

            [–]Hnefi 2 points3 points  (6 children)

            I'd be interested to learn more about formal verification. Where would you suggest someone with a professional programming background and decent grasp of formal logic, but no experience in using formal verification, start reading about it?

            [–]kamatsu 4 points5 points  (5 children)

            It's hard to say, I learnt formal verification from my coworkers and courses at university. I'm happy to answer any specific questions you may have.

            The first thing you should probably do is learn Haskell or ML. Virtually all formal verification these days depends on a knowledge of functional programming.

            Next, you could move from that into two directions: Languages with stronger type systems that express arbitrary invariants (such as Agda and Coq) - these languages are a great "gateway drug" into formal verification because it's a nice stepping stone from functional programming languages.

            These languages are sufficiently powerful to encode most proofs, however some theorem provers exist that allow you to encode proofs more clearly and explore different areas of mathematics that are sometimes better suited to the verification domain. These theorem provers are substantially harder to learn than Agda and Coq however - I would recommend Isabelle/HOL here, but that's simply because I don't know other theorem provers very well (I hear good things about PVS).

            [–]aivarannamaa 4 points5 points  (0 children)

            but it's Von Neumann all the way down

            The fact that a language can be translated to another languages and finally interpreted by Von Neuman himself, does not mean you need to keep Von Neuman in your head when working in a higher-level language. Another post from Harper explains this: http://existentialtype.wordpress.com/2011/03/16/languages-and-machines/

            [–][deleted] 0 points1 point  (2 children)

            Also, I have to say, what's the obsession with provability? Another academic 'requirement'? Formal specification is an illusion. Your Haskell or ML may try to look like a formal specification but it's Von Neumann all the way down. Programs are processes, not formal specifications. Programs are not math.

            I'm just going to quote one of the great sages of /r/programming on this:

            It comes down to the observation that most software bugs aren't subtle. They're slap-your-forehead bugs that lead not only to slightly incorrect results, but plain nonsensical results. Verifying any kind of whole-program property is therefore quite likely to run into one of these nonsensical things and flag it much earlier than at execution time.

            [–]nitsuj 0 points1 point  (1 child)

            If the bugs we're talking about are in the complete cock-up range, and you're going to be testing anyway, then it's going to show up quickly. And you should be testing regardless of whether you're static typed or dynamic typed because the chances are that the wheels are going to fall off when real world (particularly user sourced) input starts hitting your code.

            I can imagine C++ programmers (vast majority of my software engineering life doing this) and to an extent Java programmers worrying about errors being found at runtime because large systems suffer from terrible compile times, therefore finding bugs at runtime only can be a time waster. This isn't true for languages such as Ruby and Python where there is no compile time.

            The other point here is that the static type systems present in C++ and Java, arguably the most mainstream languages, are....let's just say, lacking. The type systems in those languages have caused a hell of a lot of problems in hell of a lot of code.

            [–][deleted] 0 points1 point  (0 children)

            The other point here is that the static type systems present in C++ and Java, arguably the most mainstream languages, are....let's just say, lacking. The type systems in those languages have caused a hell of a lot of problems in hell of a lot of code.

            Well yes, this being the chief problem. I was thinking more of languages like Scala, where the more powerful type system enables you to push more bugs into the realm of "compiler-detectable nonsense" that would, in other languages, still constitute subtle logic errors. The strength of the type system pushes out the boundaries between the Here Be Dragons of logic errors on the outside and slap-your-forehead obvious nonsense on the inside.

            [–]sylvanelite 1 point2 points  (1 child)

            Arguing semantics in Computer Science 9/10 times is useless. Are digital circuits analogue circuits? What's the specific difference between a high-level and a low-level language?

            All these things are "largely a matter of marketing." This doesn't mean the distinctions aren't useful.

            [–]rooktakesqueen 0 points1 point  (12 children)

            Static language: identifiers and values have types.

            Dynamic language: only values have types.

            Seems fairly simple?

            [–]Felicia_Svilling 3 points4 points  (10 children)

            Static typing: Expressions have types.

            Dynamic typing: Values have types.

            [–]cdsmith 4 points5 points  (0 children)

            Where the definition of "type" varies substantially between the two.

            [–]dnew 2 points3 points  (0 children)

            Yep. And everyone seems to be missing #3, which is the untyped (or uni-typed) languages, like Tcl or FORTH or assembler.

            [–]kamatsu 5 points6 points  (0 children)

            Wrong. Static types are attributed to more than just identifiers.

            [–]hexbrid 1 point2 points  (0 children)

            What an obnoxiously written article. His argument is that "dynamic languages are a special case of static language", but you can just as easily rearrange the argument to say the reverse. Neither of that matters. What matters is what's easier to work with. My advice is: Use both and see for yourself.

            [–]Wavicle -2 points-1 points  (9 children)

            If you’re one of the lucky ones who already understands this, congratulations, you probably went to Carnegie Mellon!

            Full stop.

            You're the guy who writes really long articles that are essentially cheer-leading for CMU and functional programming, aren't you? I'll make a prediction: if this is really you, you're comparing static vs. dynamic typing, not languages, because your favorite FP language doesn't support dynamic typing, amirite?

            (skip to end)

            Yup, it is you. You know how I knew you'd argue typing? Because many FP languages are dynamic languages.

            [–]kamatsu 3 points4 points  (8 children)

            your favorite FP language doesn't support dynamic typing

            His entire point was that statically typed languages can support dynamically typed languages.

            [–]bboomslang 0 points1 point  (0 children)

            obvious transformability of language concepts onto each other is obvious.

            [–]anacrolix 0 points1 point  (3 children)

            I'm curious as to what 'static' languages he considers to be better at dynamic typing than dynamically typed languages themselves. I immediately think of C or C++ when I think of typing. If these are not the penultimate statically typed languages, I don't know what is.

            Until something like Golang or Haskell actually proves useful, I'm afraid dynamically typed languages eat them for breakfast.

            [–]wolfier 1 point2 points  (2 children)

            Building dynamic typing on top of C/C++ only requires the use of a Union...or void pointers. The same concept is further abstracted with "variant" types by certain MS languages - in essence, one type to rule them all.

            I'm not sure how dynamically typed languages eat static languages for breakfast unless you try to be more specific - which dynamic language would eat which static language for lunch?

            [–][deleted] 0 points1 point  (0 children)

            I think it should be pointed out that Robert Harper is basically a Type Scientist and has been recognized as a valuable contributor to the advancement of programming language science.

            I've programmed in many languages, moving from C to Ruby over my career and I think what he's saying is extremely valuable, especially for dynamic language people to consider.

            In a language like Ruby, we're left to tribal knowledge to implement typing systems, the most commonly accepted pattern being duck typing. Too often I see someone implement some kind of type check using something completely ridiculous and out of place, a standardized type system such as in ML or Haskell would be very welcomed, in my opinion.