Sunday 24 July 2011

Reversed type declarations

I can't write about Kotlin without first talking about the folly of "reversed" type declarations. Sadly this disease has afflicted Scala, Gosu and now Kotlin (but perhaps its not yet too late for Kotlin ;-).

Reversed type declarations

When I see a new language, one of the first things I look at is the parameters and variable declarations. For this blog I'll refer to them as "standard" (like Java, Ceylon and Fantom) and "reversed" (like Pascal, Scala, Gosu and Kotlin).

  // standard
  Type variableName
  
  // reversed
  variableName : Type

Here I compare a Java and Kotlin method, although the principle is similar for Gosu, Scala and quite a few others.

  // Java
  public void process(String str) { ... }
  
  // Reversed lang (Kotlin or any similar language like Scala or Gosu)
  fun process(str : String) { ... }

When I see the latter, I cringe. Its usually a sign that the language isn't going to win me over.

So why the big deal?

For a start, there is one extra syntax character, the colon. Given that this is unnecessary (as shown by Java), it continues to be surprising to me that languages that aim to be less verbose than Java begin with something that is more verbose.

Ignoring the annoyance of having to type the extra character, the two examples above are still fundamentally readable. The human eye (specifically my human eye, but hey I'm being opinionated...) can cope with the extra colon and "reversed" declaration order. However, add complexity and the situation changes:

  // Java
  public void process(String str, int total, List<String> input) { ... }

  // Reversed lang
  fun process(str : String, total : int, input : List<String>) { ... }

As more parameters are added, the eye has more difficulty in picking out where one parameter ends and the next one starts. This is simply because the colon is more visually arresting than the comma, so as the eye scans the line it breaks up the parameters using the colons, not the commas. Thus at a glance I see "str", "String, total", "int, input", "List<String>". In fact, my eye sometimes doesn't see the commas at all, thus I get "str", "String total", "int input", "List<String>" which is horribly broken.

In order to actually read the information, I have to slow down and take longer. But when designing a programming language, it is rapid and quick readability that matters. Slowing me down on reading is a Bad Thing. So, beyond being unnecessary, the extra colons are actually making the code significantly harder to read (and write!).

But it gets worse:

  // Java
  public Future<Person> process(String str, List<String> input) { ... }

  // Reversed lang
  fun process(str : String, input : List<String>) : Future<Person> { ... }

Now, we have a return type, again separated by a colon. The use of the same character (yes that is another verbosity character I have to type) makes it especially difficult to visually parse. For me, the strength of the colon overrides the end bracket, thus I end up seeing "Future<Person>" as a parameter. Effectively my eye is parsing the line in a fraction of a second, but it gets to the end and has to double back to "push" the last thing it saw onto the "return type" stack. Try this one if you're struggling to see the issue. Note how the types and colons dominate and flow into one another, causing the distinctions as to their meaning (type of a parameter vs return type) to be lost:

  fun process(a : Int, b : Int, c : Int) : Int { ... }

As an aside, lets look at default parameters, which many new languages support (using an equivalent syntax for Java):

  // Java
  public Future<Person> process(String str, List<String> input, int total = 0) { ... }

  // Reversed lang
  fun process(str : String, input : List<String>, total : Int = 0) : Future<Person> { ... }

Now it is really broken! Now I've got "Int = 0" staring me in the eye, which really is not what the programmer was trying to express. Again, that visual barrier of the colon, together with the type, makes it very hard to connect the actual parameter name "total" with the value it has "0".

The real test for the syntax is the more complex case of higher order functions. This varies a lot by language, so lots of examples (hopefully accurate - I don't have time for lots of testing). I'm simulating some syntax for Java and Fantom:

  // Java
  public <T, R> List<R> transform(List<T> list, Transformer<T, R> transformer) { ... }
  public <T, R> List<R> transform(List<T> list, #R(T) transformer) { ... }  // lambda strawman
  public <T, R> List<R> transform(List<T> list, {T => R} transformer) { ... }  // BGGA
  
  // Ceylon
  shared List<R> transform<T, R>(List<T> list, R transformer(T)) { ... }
  
  // Fantom
  R[] transform<T, R>(T[] list, |T -> R| transformer) { ... }  // simulated syntax

  // Gosu
  function transform<T, R>(list : List<T>, transformer(T) : R) : List<R> { ... }
  
  // Kotlin
  fun <T, R> transform(list : List<T>, transformer : fun(T) : R) : List<R> { ... }
  
  // Scala
  def transform[T, R](list : List[T], transformer: T => R) : List[R]

The Java strawman and BGGA examples and Fantom pseudo-example demonstrate that a form can be created where higher order declarations are possible using "standard" declarations. Ceylon chooses to go down the C style route, mixing the parameter name in the middle of the return type and arguments. I don't find Ceylon's choice as readable to my eye when scanning as the Strawman/BGGA/Fantom pseudo-examples because it mixes the type and the variable name.

The three "reversed" declaration approaches are very different. Gosu (if I've read the documentation correctly) makes a very weird choice as the element after the colon is the return type of the function not the type of the variable "transformer" within the method as would be expected most of the time. Kotlin's choice is also poor, as it now means there are two colons in the parameter declaration, one to separate the variable name from the type, and one to separate the function type input from its output. Scala's is the most rational of the "reversed" declaration styles. However, I find the lack of anything surrounding the "T => R" means that the eye struggles to find the start and end of the type in more complex examples, which is essential to finding the variable name.

Of these, the Strawman/BGGA/Fantom pseudo-examples and most readable of the first group and Scala in the second group (ignoring the real Java example for a minute, and noting that Scala would be clearer with something around the function type). That is because when I'm performing the eye parsing/scanning I've been talking about, I am essentially trying to grasp the signature. To do that I need to know the number of parameters, their names and their types. Specifically, I want to put the name and type into different mental boxes. Mixing the name and type as Ceylon and Gosu do makes that harder, while Kotlin's additional colon simply creates another fence for my eye to have to jump.

To do this full justice, I should really have some examples of function types of function types. However this blog is already very long...

Finally, I'll point out that this isn't just confined to method parameters, but also to variable declarations such as local variables. Again, this gets complicated between language in the detail, so I'll compare to a typical "reversed" type language using a braindead stupid example:

  // Java
  private String process(int total) {
    String str = Integer.toString(total);
    return str;
  }

  // Reversed lang
  fun process(total : Int) : String {
    val str : String = Integer.toString(total)
    return str
  }

Again, the "reversed" example is telling me that "String = ...", not "str = ...". In logical terms, its utterly broken.

OK, OK, I can hear you yelling "type inference":

  // Java
  private String process(int total) {
    val str = Integer.toString(total);
    return str;
  }

  // Reversed lang
  fun process(total : Int) : String {
    val str = Integer.toString(total)
    return str
  }

So, I cheated right? By inventing a Java type inference syntax. Well, I'm making the point that type inference need not be limited to new languages, Java or any language using the "standard" type declaration style can have it too (and Fantom and Ceylon do). Thus, we should judge the variable declarations by the long form, even if it is not used for local variables all the time. And as shown above, the long form is awful in the "reversed" style. I am most emphatically not assigning the total to "String", I'm assigning it to "str", and that is what the code should say.

I'm sure if you've read this far you have a number of comments. Perhaps you believe tooling solves the issue, maybe syntax colouring? Well, I'll simply say that while tooling helps, you should still be able to understand the language without it, even if just for command line diffs around a version control system. Or perhaps you're objecting to my methodology of trying to visually parse a line in a glance? Its how I work, don't you do scan code too?

Let me be clear, in none of the above do I mention the task of the compiler. My sole focus is on the developer reading the code, and an order of magnitude less writing code.

Any argument in support of "reversed" type declarations should never be based on relevance to the compiler or some other element of type theory.

My view is that the usability to the mainstream developer is what matters. And that is primarily about ease of reading what is written. I have endeavoured to show that the reverse style hampers readability, and is unnecessary to achieve the same goals of a more complex type system that are sometimes used as justification.

Summary

I'm arguing that the "reversed" type declaration style is flat out harder to visually parse, and should therefore be rejected by language authors, even if they believe they have sound compiler or type theory rationales. Programming languages exist primarily for developers, not to aid the compiler or underlying theories. "Write once, Read many" must be the first law of language design!

I am thus hugely disappointed that Kotlin, which has many fine features taken from Fantom, did not think through this choice in more detail, and I plead with the authors to change their minds before Kotlin is locked down.

Opinions welcome, and I'm sure there will be lots...

44 comments:

  1. This is a very interesting definition of "standard". One could just as easily assert that postfix type annotations are the standard and that Java et al are "reversed".

    Here is my counter assertion: the style you prefer is only more readable to you because you have been using languages of the C family for most of your working life.

    For those who have learned other languages, and for those who have not yet learned to program at all, neither style is more readable or "standard" than the other.

    ReplyDelete
  2. Hi Stephen,

    First of all thanks for the detailed article. It really helps to have a precise comparison of the languages instead of some of the exchanges we've seen in the past few days.

    On the readability side you say that

    def f(name : String, i : Int)

    is not very readable because of the semi-column stopping your eye. I must say that I don't like it too and that, for that reason, most of the Scala code I see (and write) is:

    def f(name: String, i: Int)

    So the readability is good to me.

    On the other hand I sometimes wished that parameters with default values were inferred:

    def f(name = "", i = 0)

    Also I disagree with you on judging the "reversal" declaration based on the "long" form. We should compare actual programs and not potential ones when comparing readability. And because the long form is not used most of the time, it is ok to me. I'll even go further and say that most of the time, when the type is specified, it is because it *should* catch the eye of the user. For example, when you want to force an implicit conversion:

    val a: Arrow[Int, String] = (i: Int) => i.toString

    Eric.

    ReplyDelete
  3. "Why types on the right?
    We believe that it makes the code more readable. Besides, it enables some nice syntactic features, for example, it's easy to leave type annotations out, and Scala has proven pretty well that this is not really a problem."

    That's from the Kotlin FAQ. I'm not saying they're right, but they seem to think it's _easier_ to read and, it's trivial to leave them off when types are inferred.

    ReplyDelete
  4. Could you please tell us the reason why you put a space *before* the colon in your examples? Maybe you would find it less hard to read when you'd actually use the recommended formatting style of these languages (Scala at least).

    Otherwise we have to assume that you do it that way just to support your argument.

    Or are you one of those types that also put a space before commas? Or no space after it?

    ReplyDelete
  5. Having used both styles extensively I really hardly notice a difference. My personal preference is for Haskell's style, something like:

    process :: Int -> String
    process total = show total

    (trivially rewritten as process = show)

    There the type is written before any of the names (though the entire type declaration is optional), and there is some difficulty in matching them up occasionally but I think overall it adds to readability.

    I suppose you could even horizontally align parameter names and types to make it easier to match them up.

    ReplyDelete
  6. Re my last comment: Just saw that the Kotlin examples put a space before the colon. Ah well, this will probably change pretty soon as the language develops and is used for more than just Hello World examples.

    ReplyDelete
  7. Robin, that is the style that Kotlin is using. Stephen didn't just invent it to make postfix types look bad.

    And hey, I wonder if the very fact that you need an extra coding standard to decide whether to put spaces before/after the : is itself an argument against the colon!

    Anyway, something I don't quite get about the colon is that it is actually pure noise. AFAICT, it doesn't help the parser at all. It should be possible to do postfix type annotations *without* colons, but I don't think I've ever seen any language that actually does that.

    Why aren't there any languages that use:

    void process(str String) { ... }

    I think that some of Stephen's arguments would be weaker if it weren't for his well-taken observation that colon has a heavier visual weight than comma. So why not just eliminate the colon altogether, if postfix types are so great?

    ReplyDelete
  8. This is much ado about nothing, the epitome of a butter battle. People who obsess about syntactic trivialities like this are missing the real issues that differentiate languages.

    ReplyDelete
  9. Just say no to floating colons! You don't do it in English, you shouldn't do it in code. Getting rid of the floating colon makes these type declarations much more readable.

    ReplyDelete
  10. Looks mostly a familiarity issue. In math, one writes first the name and then the "type" and it has been common use for a couple of hundred years at least.
    After 15 years of C/C++/Java (what I would call "reverse" declarations) I'm learning Scala, and I'm loving the id:type style.
    What really irks me out is the "< >" notation for type constructors... I cannot stand it, the symbols take a LOT of space making the line unnecessarily long.
    I guess you kind of proved my point with your "transform" declaration: the visually shortest is the Scala one.

    ReplyDelete
  11. Gavin, actual there is a language that uses postfix types without the colons, and that is the Go programming language.

    ReplyDelete
  12. Dude , do not put a space before the colon . If you do that , you really do make it harder to read , since you turn them into separate tokens . Just imagine if normal English punctuation was used like that , for instance.

    The only place where the colon was used correctly was in Scala's high order function example . Curious , because you just happen to have found that easier to read than Gosu's and Kotlin's .

    Other than that , it should be noted that on of the reasons why Odersky choose to put types second was to draw attention to the identifier (http://stackoverflow.com/questions/6085576/why-does-scala-choose-to-have-the-types-after-the-variable-names/6087167#6087167) . And , on the same note , it is common when writing Scala to break the parameter list of methods with multiple parameters into multiple lines .

    ReplyDelete
  13. Readability is in great part subjective / dependent on practice - our brain and visual system are exceedingly good in automating recognition of patterns, as you know, so most reasonable patterns (regular and unambiguous) become "natural" after some time. So, your TL;DR argumentation about readability does not impress me ;-)

    You get a point that Pascal-style is not good for adoption but that's just because the dominant languages *today* are all C-style. But this doesn't say anything about the intrinsic value of type declarations for adoption (independent of the dominant style du jour). Pascal was once upon a time pretty popular, and Visual Basic was enormously popular (still is??) and it doesn't seem that type declaration style was a problem for either language...

    Be careful to dismiss things like type theory as academicism that should be regarded as low priority after usability. Programming languages are not GUIs; usability is certainly important, but strong language design will always be critical. Ask language designers to ignore the textbooks and you end up with monstrosities such as Perl. It's not a matter of making the compiler easier to write; it's a matter of making things consistent, orthogonal, flexible/extensible, future-proof, simple not just in v1.0 but 10-20 years down the road, etc.

    ReplyDelete
  14. Use smaller colons, bro.

    ReplyDelete
  15. I've not minded pre or postfix syntax from Pacsal up through modern languages. But with either, a colon really is superfluous and Stephen's argument that it is more visually arresting than a comma in a parameter list is dead on.

    Ditch the colon.

    ReplyDelete
  16. Stephen, here's another argument - using postfix types kills the IDE's variable name proposals. You can't type Person p anymore.

    ReplyDelete
  17. I find these discussion superficial and of no relevance really. You are entitled to your opinion and I believe that experience plays a great role on this but unless someone actually provides scientific data on prefix or postfix declarations and their readability I don't believe it.
    I myself can read scala and java fluently and have no issue whatsoever with either declaration so I do not see the problem you are stating.
    So please people provide some evidence about the claims (links anybody) or let us stop these silly discussions.

    ReplyDelete
  18. Preference of syntax is very subjective, but I get the feeling that this is very much a "Java doesn't do it this way, it just looks strange and is therefore wrong" perspective.

    There are various reasons for the postfix syntax are, in my opinion, sound. How many times have you looked at some javadoc, completely ignoring the type information looking for a specific method name? I know that I do that a lot and it's important to skip the fud and get to what you are looking for.

    The different syntax is just that, different. Its by no means "more wrong" or "more correct". Additionally, making accusations that it makes the IDE more difficult to complete is simply not valid - it just means that the guys developing the IDE / plugin(s) need to rethink the current way things are done. Many ways to skin a cat, but end of the day all the plugins will support the syntax - that will be needed to make them succeed.

    ReplyDelete
  19. Ditch the colon and use something else There is another static/dynamic hybrid language that uses 'as' for the delimiter. So there is precedent.

    ReplyDelete
  20. I guess this article is triggered by the heated discussion that just happened at Gavin King's blog (about Ceylon), partly about this syntactic point.
    At least, the comments above are, so far, no flame war... :-)

    Well, as you write, it is opinionated, so there is little to argue. Starting with the usage of standard vs. reversed... Amusing provocation, I think. The suffix notation exists in languages predating Scala, so beside, perhaps, number of programmers using a notation daily, it is hard to make one more standard than the other.

    Personally, I learned programming (beyond TI-57 code, Basic and assembly language) in Pascal. When I discovered the C language, I was relieved to discover a terse syntax! I loved it. I coded in C a lot, in C++ a bit, and in some C-like languages (JavaScript, PHP...) but they are quite irrelevant for this discussion, and today mostly in Java. So I can say this syntax is the most familiar for me.
    I wrote some tools for text editors: lexers for syntax highlighting, code to list the functions of the current file, etc., and I was less pleased: it is harder to parse such syntax, as anything can be used as prefix for variables or functions. Having a smaller set (var, val, def, fun, etc.) makes life easier...

    Anyway, I recently started to learn Scala, and I was quite pleased by this syntactic choice. I find it natural and logical. Despite having my eyes trained by years of C/Java coding... And, of course, it is a personal, opinionated view, like your...

    I don't feel the colon is coming in the way, even more as I write it sticking to the variable name, in the English tradition, even if I am French (we use an half space before the colon): after all, I write all my code in English, so I respect the English typographical conventions. BTW, the same goes for semi-colon (we put an half-space before all punctuation sign in two parts: ?!:;«»). Exception: usage of in Java: for (:); assert a : m; b ? a : b; etc.

    Of course, we can get rid of the colon. Hey, we can even get rid of the comma, too! A compiler can go without it. But still, they are useful as visual hint, like punctuation in natural languages is useful. Beside resolving some ambiguities, it helps as a visual rhythm marker.

    As written above, all this is more matter of education/habits (and taste, of course) than of objective measure of readability.
    We can argue over this convention like we can do over tabs vs. space, vi vs. Emacs (I am neither...), placement of braces (aligned for me, thanks) and so on.

    That's why there are so many languages: beyond the base choices (dynamic/static, imperative/OO/functional/etc., compiled/interpreted but this one is mostly void in current times), syntax is a quite important criterion of choice that, indeed, cannot be dismissed.
    Choice is good... (but choice might be reduced when you code for a living...)

    ReplyDelete
  21. I'm inclined to agree that the primary thing to take away from this article is that the author has been using Java for a long time, and it's unreasonable to expect that an eye trained to Java's way of doing something so fundamental as specifying types will find a different way of doing it easy to interpret at first.

    However, it seems to me that the primary market for Scala and Kotlin is Java developers. If the pre/post fix argument is largely an unimportant syntax one from a language perspective, it becomes quite an important one from a chance of success perspective if a lot of Java developers end up rejecting using the language because of it. It doesn't matter how sound the arguments for postfix types are if the result is that the language doesn't see much take up.

    ReplyDelete
  22. *Sigh*

    There are two types of persons: Those who have understood why it is a good idea and those who are still arguing against it.

    Progress is sometimes slow in the PL community, but I'm glad that common sense is slowly winning here.

    Sad that some "language designers" still don't get it (*cough* Ceylon *cough*), but in the end it seems that all future language converge on the right design decision (could we just drop that butt-ugly space before the colon?).

    ReplyDelete
  23. (Maybe this can be merged with my earlier post ...)

    a) "So, I cheated right? By inventing a Java type inference syntax. [...]"

    You are falling into the same trap here as the C# people (who did it that way). Sure, it can be tacked on top of an existing language with C-style type declarations, but does it make sense?
    No. This destroys the syntactic clarity of a language, because there are now to different things which can appear at one position, either a type _OR_ some special keyword.

    b) "However, I find the lack of anything surrounding the 'T => R' [...]", well then maybe just add parens around it?

    def transform[T, R](list: List[T], transformer: (T => R)): List[R]

    (And please, drop the spaces before the colon in the Scala examples.)

    ReplyDelete
  24. I asked Martin Odersky about his choice of syntax in Scala type declarations. He gave a detailed answer here: http://drdobbs.com/231001802 Scroll down to the section entitled 'Syntax.' The choice was carefully made and was a conversion from your preferred syntax.

    ReplyDelete
  25. Honestly I have never seen in my life a so long post on a so futile topic.

    ReplyDelete
  26. Stephen Colebourne25 July 2011 at 12:57

    Good to see so many comments. The most frequent point made was that Scala has a coding convention to omit the space before the colon. I agree that this helps to some degree, but I still consider the colon to be "in your face". Remember that the first key point in the blog is that the colon is more significant visually than a comma. Gavin also correctly questions why the colon is needed at all in the "reversed" style.

    I'm also amazed that so few commenters see the irony of using "adjacent colon" as an argument. By requiring the colon to be placed next to the variable name you are essentially making my case - that the colon is too intrusive and has to be placed next to the variable to minimize it!

    However, no commenter has addressed the more fundamental issue of default values in parameters and assignment in variables, beyond the claim that its so rare with type inference that it doesn't matter (er, default values in parameters anyone...?). The example from Eric is a classic:

    val a: Arrow[Int, String] = (i: Int) => i.toString

    Firstly, its visually hard to distinguish that before and after the assignment = (because of the =>). Then it seems that the coder is assigning the function to a variable of "Arrow". It is very, very hard to connect the assigmnent of the expression back to the actual variable "a", and it involves considerable eye scan effort.

    Finally, I'm disappointed with comments implying it is *just* about familiarity with Java. My arguments are based on real readability points (visual colon size vs comma, and the absurdity of assigning an expresion to a type not a variable). This is not just a vague waving of hands. I'd prefer to see real arguments to justify why those two points are wrong or don't matter. Saying "you'll get used to it" doesn't help.

    ReplyDelete
  27. You can find here another good explanation of why Scala has the postfix typing syntax http://relation.to/Bloggers/CeylonProgressReport#comment21215

    ReplyDelete
  28. @JBaruch: Well, that auto-complete relies on Java requiring you to write the type: "Person person =". In Scala (or Kotlin), "val person =" does not have such repetition, which precludes the need for auto-complete.

    @Stephen & others: Speaking of Scala only, the colon denotes an _optional_ type ascription. It just so happens that it is not optional in some places, but its use is consistent.

    val x: Int = 5
    val x = 5: Int
    val x = 5

    def plus5(y: Int): Int = y + 5
    def plus5(y: Int) = y + 5

    If colon was not required, then this statement would be ambiguous:

    val x = 5 Int

    The problem being that "Int" can be a type or a method.

    Finally, yes, ":" has a visual footprint, but none of us consider it any more distinct than ",". I don't see you complaining about commas -- even though they are superfluous, particularly in Java --, or talking about how their usage without preceding space gives credence to your arguments.

    Naturally, colon will draw more attention of you because its usage is unfamiliar to you. You are just not used to it.

    Conversely, try programming without a few months without *semi*-colons and see how much they stick in your face!

    Your argument is very good: syntax should make code clear. However, it is unfamiliarity (and that preceding space) that is making colon visually distracting, not the colon itself.

    ReplyDelete
  29. > Finally, I'm disappointed with comments implying
    > it is *just* about familiarity with Java. My
    > arguments are based on real readability points
    > (visual colon size vs comma, and the absurdity of
    > assigning an expresion to a type not a variable).
    > This is not just a vague waving of hands. I'd
    > prefer to see real arguments to justify why those
    > two points are wrong or don't matter. Saying
    > "you'll get used to it" doesn't help.
    As asked before could you please provide links to scientific data (not just assumptions on your part) that actually proof that prefix or postfix interfere with readability? Until then I do not believe that either is better for readability.

    ReplyDelete
  30. Two additional reasons why people put a colon between identifier and type:

    a) Because it is established in literature for decades already, like using [] for type arguments and <: for subtype relationships.

    b) Google Go uses and god, it looks so ugly, that almost every other language shied away from using it.

    ReplyDelete
  31. I wish Kotlin reversed this and the semicolon insertion. Otherwise, I prefer Ceylon over Kotlin for readability / familiarity.

    ReplyDelete
  32. After a decade of Java, it's a bit unsettling to go back to the Pascal way of declaring, but in my experience, it only takes a couple of weeks for this new syntax to completely disappear, both from a reading and writing standpoint.

    When the creators of languages using this syntax are asked, I seem to remember they typically answer that it makes the type inference syntax easier for developers and compilers alike. Stephen, I didn't see you mention this at all, you might want to look into that.

    Finally, ever since I started using C, I have been wanting type declarations that flow more as an English sentence. From this standpoint, in order of worst to best:

    int[] f "int array f"
    f: int[] "f is an int array"
    f: []int "f is an array of int"

    That last line is the most uncommon but on the surface, appears to be the most intuitive to me.

    At any rate, Stephen, you should definitely not let this kind of syntactic detail stop you from adopting a language, you will be surprised at how fast the human eye can accomodate various syntaxes and coding conventions.

    ReplyDelete
  33. Hi Cedric,

    > f: []int "f is an array of int"
    >
    > That last line is the most uncommon but on the surface, appears to be the most intuitive to me.

    Are you really sure?

    Because this is also the way Scala does it.

    f: Array[Int]

    It even spells it out and doesn't use any weird magic operator overloading like Java and the others.

    ReplyDelete
  34. Steve,

    Are you seriously asking me if I'm sure that a certain notation looks the most intuitive to me?

    Well, er... yes. I would know, wouldn't I?

    ReplyDelete
  35. > Are you seriously asking me if I'm sure that a certain notation looks the most intuitive to me?

    Yes, I was just a bit surprised that you did agree with some design aspect of Scala and wondered if you did that maybe by mistake.

    Thanks!

    ReplyDelete
  36. Steve, I still have no idea what you're talking about, but... whatever.

    ReplyDelete
  37. Stephen Colebourne26 July 2011 at 10:13

    I note Daniel Spiewak comment in the ceylon thread (http://relation.to/Bloggers/CeylonProgressReport#comment21215) of "And, speaking as someone who has done a fair amount of research in type theory and formal logic, postfix types are quite a bit more in line with standard mathematical notation" which in many ways makes my point. Steve's comment of "Because it is established in literature for decades already, like using [] for type arguments and <: for subtype relationships" does so as well.

    I am of the opinion that language designers, who are typically heavy readers and writers of the academic literature, are simply exposing what they know without full consideration of the impact on the far far larger group of language users who do not know or care about type theory and the academic side of programming. We need to respect and utilise academia, but design of a mainstream language should be around what is most of value to the user, not the language author. Those two things sometimes align, and sometimes not. For example, <: may be obvious to much of the blogoshpere who know more type theory than average, but from my experience the vast majority of developers don't even understand (or care about) variance. As I've said before, usability to the mainstream (non blogosphere) is the great frontier in language design, not type theory, functional programming or other similar elements.

    On the semicolon point, I agree that lines should not need to end in semicolons! Java is far from perfect.

    On the academic reaserch point, I'd love to see a research paper on the *visual* readability of colon, comma and alternate type/variable ordering. If it proves me wrong, I'll happily reduce the scope of my claims to personal preference. (I actually think this could make a good PhD topic, although almost more in ergonimics than copmuting.

    @Cedric, I think the "f: int[] --> f is an int array" point probably deserves a separate blog.

    ReplyDelete
  38. Well, regarding the scala type declaration, it always felt natural to me since its optional. As such you have a consistent declaration scheme:
    val/var variablename(: optionalType )
    you can always figure out quickly the variablename (where most of the time you skip the type info anyway).

    for me, it was not a big issue when I played a bit with scala, one just has to get the habit.

    regarding variance and the like, java really got it wrong and still we assume java to be the easy & not complex beast... so this kind of assumption is rather weak IMHO

    furthermore, in scala, with type being handled on the class definition and not on each of its uses, I guess variance needs to be understood mostly by API Designers

    for the rest of the crowd, the "I don't care, I just write crap code" kind of dudes, I guess type inference will make them very happy and the few occurences where variance could have helped them they'll just miss it, like 80% of anything they use anyway. Or they'll learn, but then thanks god they're coders with a brain, who I really welcome and which make them another crowd (more akin to ones I love to work with)!

    ReplyDelete
  39. BTW, I really think odersky has a point there as well: http://stackoverflow.com/questions/6085576/why-does-scala-choose-to-have-the-types-after-the-variable-names/6087167#6087167

    this:
    val shapeInfo: HashMap[Shape, (String, String)] = makeInfo()
    is way more readable than:
    HashMap> shapeInfo = makeInfo()

    and what if it's a var? Do you do this:
    var HashMap> shapeInfo = makeInfo() ?

    Then you got one extra convention on top that no val/var declaration defaults to val... My goodness!

    ReplyDelete
  40. @josephpachod: Your comparison is not apples-to-apples

    this:
    val shapeInfo: HashMap[Shape, Pair[String, String]] = makeInfo()
    is *not* way more readable than:
    HashMap[Shape, Pair[String, String]] shapeInfo = makeInfo()

    To your second point, you can default to var (like Java) or to val (like Ceylon) and then you only need one keyword instead of two.

    ReplyDelete
  41. @Dan:
    I completely disagree.

    With val/var/def you have the guarantee that the identifier will always be at the same position allowing you to read the code much faster. This was one of the key arguments why Scala decided to do it that way.

    Having the type come first doesn't have these benefits because they will always take up different amounts of width.
    Additionally the syntax gets inconsistent with that approach if you later introduce type inference here. C#'s "var" syntax is a backward compatibility hack, which should have been avoided by "new" the languages.

    ReplyDelete
  42. What a sad state we are in when the suitability of a language will be judged by such a minuscule and arbitrary thing like the position of types in declarations ... In a non English centric universe, you are WRONG as Boolean := true ...
    The sad thing is you will win, your opinion is part of the not-so-silent majority that is going to impose on us the PC (Political Correctness) of computing. I need to start building my cross-compiler as of NOW! Otherwise I feel I'll burned at the stake of XXI century "Enlightment".
    Do we need to post here the rule to read return types on C functions? Are the marketing sins of Java going to ask for our unborn children-languages?

    ReplyDelete
  43. And outside of the JVM (http://blog.golang.org/2010/07/gos-declaration-syntax.html) those Go guys sure are a bunch of ignorants that started programming yesterday ...

    ReplyDelete
  44. Which is more *regular* (especially if you have type inference)?

    Java
    int myvar1 = 42
    Map> myvar2 = ...

    Scala
    val myvar1: Int = 42
    val myvar2: Map[String, List[Int]] = ...
    val myvar3 = ...

    With a list of variables like this, my eyes have to move all over the place in the Java version. I think Scala and Kotlin picked this form for good reason.

    ReplyDelete