Showing posts with label jvmlang. Show all posts
Showing posts with label jvmlang. Show all posts

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...

Thursday, 21 July 2011

Kotlin and the search for a better Java

Another day, another new JVM language. This time Kotlin.

My first point here is that another group is willing to go public and say that Scala is too complex. It is easy to miss this, but anyone writing a new language right now (Kotlin, Ceylon, Gosu, Fantom, ...) is implicitly saying Scala isn't right. Of course I don't expect Scala supporters to like this or agree with it, but the truth is that I and many others have looked at it and run fast in the opposite direction.

As I've commented before, my dislike for Scala is not support for Java. There is a lot wrong with Java, and that cannot be sorted out without breaking backwards compatibility. Elements like primitives, arrays, wildcard generics and basic operators like equals. That is why I have proposed a backwards incompatible Java - #bijava.

More generally, my position is that as a community there is a role for a popular, statically typed, industrial, Java-like langauge without Java's warts. The Java-like also means a design that manages complexity and is usable by the mass-market. Fantom, Gosu, Ceylon and Kotlin are targetting that market. Groovy, Clojure, Scala and many others are simply not targetting that specific market.

I can't comment that much on Ceylon as it is more vapour than reality and from the little public information appears to have some dubious design decisions, especially around verbose words rather than syntax. I've also not studied Gosu much for some reason, yet in a day I've looked at Kotlin a lot (not sure why Gosu doesn't excite me...).

Fantom is probably the most different of the four. It provides a new platform, which happens to run on the JVM (and in the browser via Javascript!). The core Fantom library is new and dedicated to Fantom, with some different design principles to those of Java. It also runs its own form of bytecode, allowing deep immutability, non-null types, full modules and reified generics amongst other things. In fact, one could question whether Fantom fits into the group of four at all, however it does fit the criteria of statically typed, industrial and Java-like.

So, what about Kotlin? Well at first glance it gets a lot right, starting with null-safety and type inference. However, I have real issues with some features, which I think a proper Kotlin blog piece should focus on.

More generally if I could wave my magic wand, I would probably wish that Ceylon and Kotlin would merge into a single project. (Gosu and Fantom are now used in production, and Fantom has many different goals, so both are harder to change now.) Basically, we need the energy of all that input (and associated money) but with a better, single focus. Both Ceylon and Kotlin are still mostly paper languages, and both are trying to achieve the same thing (with Kotlin looking closer than Ceylon at this point). RedHat and JetBrains could you please have a conversation? (I'm happy to mediate if desired.)

Summary

I still like Fantom and I think most people hugely underestimate it, especially those from a Scala background. Fantom's rethinks what a language should be from the platform/productivity perspective with deep immutability, deep modules, no shared state and a practical type system that aims to eliminate bugs not be in absolute control. Scala is in many ways, light years behind Fantom.

And this is my key point with Kotlin too. Simply focussing on syntax is worthy, but kind of misses the point. Syntax exists simply to express the programmers intent in a way that should be readable years later. What makes a bigger difference are the productivity issues that are not typically thought of when talking about a language - versioning of code, which logging library to use, how to access configuration or injected state. Kotlin tackles the syntax parts pretty well, though not perfectly. But its not clear yet if they can grasp just how unimportant the syntax is relative to language related productivity gains.

Thursday, 22 July 2010

The Next Big JVM Language talk JavaOne

I'm talking at JavaOne 2010 on the subject of the "Next Big JVM language" (S314355). I suspect that it might cause some controversey!

Talk

Before writing the talk, I wanted to get some feedback from the community. So, I've got some basic topics and questions I'm looking for feedback on.

1) What makes a language big and popular?
Lots of people have drawn up lists - Steve Yegge has one (take a look!). But what criteria do you believe make a language big and popular? Static or dynamic? Fast or just fast enough? Lots of features or few? Simple or complex? Does syntax matter? etc. etc.

2) What might be different between a big JVM language and a big language in general (without the JVM restriction)?
Dot NET has several new languages, but they can't be considered. However, if you've seen an interesting idea then thats interesting here.

3) Is it important to reuse existing Java libraries?
Java has a big ecosystem. How important is reusing all those libraries. Or would a fresh start with a new higher quality (less cruft) core library be preferred.

4) What languages should I consider?
I've got Groovy, Scala, Fantom and Clojure on the list, but what else? I'd love to see blog posts by language authors tackling the question of why their language matches the session title.

5) If its none of the above, why not? What are you looking for?
While a random wish list of features is interesting, remember that this is about the next big JVM language. Does your idea fit that?

6) Can Java reinvent itself?
What about java itself - could a reinvigorated Java retain its crown.

Summary

Feedback is definitely wanted, by comment, email or new blog posts. Its a broad topic, but the more feedback the better (or more controversial) the talk will be! (And I'll be starting writing this weekend...)

Thursday, 12 June 2008

The Fantom language - Is it JavaNG?

For many moons there have been discussions about what we need to do to 'fix' Java. But what might the language look like if we applied all the possible fixes?

The Fantom programming language

Many (all?) of the ideas for creating a 'better Java' are not new, and have been talked about many times over the years. Ola Bini produced one list, which included no primitives, enhanced generics (no angle brackets), closures, method references, implementation in interfaces, some type inference, no checked exceptions and non-null variables. This is a good list and mirrors many of the discussions that have been held on this blog.

Of course, this JavaNG/Java3/BetterJava language doesn't exist does it?

Well, perhaps it does.

The Fan Fantom programming language is one I've been watching for a couple of months since it first launched its website. Its statically typed like Java and Scala, although there are some dynamic features.

I've been meaning to blog about it for some time, but Cedric beat me to it (and its a good writeup too).

Basically, Fantom fixes 95% of the pain points in Java in a manner and style that is close to that which you'd naturally pick if you were creating JavaNG/Java3/BetterJava. Here is a quick rundown of the key features and changes:

Pods, Types and Slots

The basic unit of grouping code is the Pod. This is like a combination of a Java package and module. It forms a key part of the name of any type in the same way as the package name does. But because it is also the module, it is easy to find missing classes - its like every class file knowing what jar file it is stored in. There is also a module level access specifier ('internal'), which is a cross between package scope and proposed 'module' scope in Java.

The two main types of type are classes and mixins. Classes are as expected, with single inheritance. Mixins are like interfaces, but can have implementation too, providing convenient and safe multiple inheritance if required.

Slots are the contents of classes and mixins. There are only two types of slot - fields and methods. All fields behave as properties, and are accessed via methods. You don't need to manually write the getter/setter, but you can if you need to, such as with calculated fields. Methods are pretty much as expected, except that you have to declare them as virtual in order to allow them to be overridden (and you have to declare override if you are overriding).

One key point is that all slots have unique names. This means that a field cannot have the same name as a method. This is a Good Thing, and allows method/field references to just reference the slot name (contrast this with the FCM method reference spec).

It also means that methods cannot be overloaded! But this is also a Good Thing, when combined with default values for method parameters, for example either of the last two arguments may be omitted and will default to zero:

Date withDate(Int hour, Int minute, Int second := 0, Int milli := 0) {
  ....
}

The totality is a very simple, but consistent, basic structure.

Constructors

Constructors are defined like static methods, but use the method name 'make' by convention. This method name and design is designed to allow easy switching from creation of objects to returning cached objects (or specialised subclasses) without affecting the caller:

class Date {
  new make(Int hour, Int minute, Int second := 0, Int milli := 0) {
    ....
  }
}

The 'new' keyword indicates that the method is a constructor.

Assignments and operators

All assignments use the := symbol. This clearly separates them from any other usages of =. It also enables a basic form of type inference when declaring local variables:

  value := 6
  str := "Hello world"

Note that there was no need to declare the variable type (Str/Int). Declarations of fields still require the type to be specified though.

Fantom also supports operator overloading by delegation to methods. Thus, writing a method with a special name will cause the matching operator to be allowed.

As a result, there is no need to call a.equals(b). The == operator is mapped to the equals method - this one change makes code a lot neater.

Immutability and Threading

Immutability (using the 'const' keyword, although a different meaning to C) is built into the language. Fields and classes can be immutable, as these gain extra power in the language.

The main benefit is with multi-threading. The language prevents there being any shared mutable state between two threads. Only immmutable objects may be freely passed between threads. If you need to pass a mutable object to another thread, then it must be serializable (using the built in serialization) and it will be sent to the new thread as a serialized message There is thus no synchronized keyword.

Collections and Closures

Collections and Closures are the only places where anything like generics appears. The collection classes have dedicated type signatures, which look like Java array signatures:

 String[] listOfStrings := ["one", "two", "three"]
 String:Int[] mapOfStringsToInts := ["one": 1, "two": 2, "three" : 3]

Note that both the list and map also have a literal syntax to define initial contents.

Closures are built in, and the API is written to work with them. The syntax is modelled after Ruby, but is pretty readable to a Java developer, using pipes to define the closure arguments:

 list := ["one", "two", "three"]
 list.each |Str val, Int index| {
  echo("$index = $val")
 }

Note how local variables can be accessed and output from within a string (like Groovy amongst others).

Dynamic coding

Fantom encourages more dynamic coding styles. If you use the dot to call a method (as per normal Java) then the method is compile time checked. If you use the arrow operator, then the method is called by reflection. Further, if the method called by reflection doesn't exist, then this error can be caught (the 'trap' method) which is like the 'method missing' concepts in other languages and enables powerful DSLs to be written.

  obj.doStuff()   // compile-time checked call, as per Java
  obj->doStuff()   // runtime reflection call

This can also be thought of as enabling duck typing.

API

Fantom has its own API. It does not directly expose the Java API, although you can access it via the 'native' keyword. The benefit if this is the removal of all the bad and broken parts of the Java API.

The Fantom authors philosophy is to provide all the useful features of the Java API, but in less classes. Thus, the IO API consists of just 4 key classes, with byte/char operations on the same class, and buffering assumed.

Odds and ends

Lines do not need a semicolon at the end. A newline will suffice.

There is built in serialization to a JSON like syntax. This is actually a subset of the language, and can be used to initialise objects at creation time in normal code.

Methods may have convariant returns like Java. But they can also have Self type returns.

Methods can be declared to run 'once', and then cache their value.

If, while, and basic for loops are the same as in Java. Switch statements are better, as they can't fall-through.

Exception handling is as per Java, with try, catch and finally. All exceptions are unchecked (yay!)

The default type for numbers with a decimal point is a BigDecimal equivalent, not a double equivalent. This will mean a lot fewer numerical errors.

Fantom runs on the JVM and the .NET runtime. It manages this by writing to a temporary intermediate format, which then gets further compiled to the right bytecode.

The website is really good and detailed.

Comparing to Java

Fantom is not Java, it is its own language. Yet, it is many, many ways the natural result of applying all the changes that the blogosphere asks for in Java. Just for that reason it pays to evaluate it closely.

Is Fantom a good language? The answer is a qualified yes. At the moment, it looks like a very well designed language I have very few points of contention with it - the main one being there is no non-null support. Minor points include some choices for coding standards (open curly brace positioning, and when to use method parameters).

Is Fantom a new language? Not really. It is a consolodation language, which is what James Gosling claims Java was (ie. a language that doesn't invent much that is radically new. Most of the ideas have been seen elsewhere, but Fantom has a particular JavaNG feel about it.

What are my key features? Built in modules, immutability and safe threading, closures, and a really great solution to generics (ie. only support them on Lists, Maps and Closures). The local type inference, no semicolons, == for equals and interpolated string are four minor features that make a big difference too.

Summary

Fantom is worth checking out. Whether it suceeds as a language is up to many factors - it needs an IDE for example, and a lot of luck.

The key point for me is that Fantom represents much of what JavaNG/Java3/BetterJava would look like if all our ideas were adopted. And while it has many similarities to Java, there is also quite a sense of difference. Perhaps, the biggest aspect of this is that the APIs are different. But that is perhaps inevitable if you want to get any real benefit from closures and fixing generics (by simplifying them).

And that perhaps gives us the definition of where JavaNG/Java3/BetterJava ends and BeyondJava starts. If the language is based around the Java APIs, its a JavaNG/Java3/BetterJava language (eg. Groovy). If the language has its own APIs, its a BeyondJava language (eg Fan, Scala).

All opinions welcome on Fantom and the BetterJava to BeyondJava boundary!

Friday, 2 May 2008

Enhancing Java - Multi-lingual blocks

The reality for Java is that there are many other programming languages, and many of those have features that Java developers sometimes wish they could access. But its simply impossible to add all those features. Is there a possible alternative if we think 'outside the box'?

Multi-lingual

What I'm thinking about in this blog is the possibility of embedding Groovy, Ruby, Jython or Scala code directly within Java code.

Why might that be useful?

Well each language has their own benefits, whether Scala's functional style or Groovy's GStrings. Including a small part of another language within the main code body could be useful, although obviously this would be a technique to be used with care.

And it doesn't have to stop at known languages. What about a dedicated 'SQL language'? Or a dedicated 'XML language'? These would be more than just DSLs, but actual languages with whatever syntax rules are most applicable.

So, what might a syntax look like:

 public String fetchRow(int id) {
   :groovy: {
     println "Row id: $id!"
   }
   :sql: {
     SELECT %text% FROM my_table WHERE row_id = %id%;
   }
   return text;
 }

The idea is that a block of code, surrounded by curly brackets, can be identified as belonging to a different language. In this case I've used the syntax of the name of the language (which would have to be imported) surrounded by colons. Note that there is nothing specific about the syntax within the block. Bear in mind that the syntax isn't that important - its the concept that matters.

The Groovy example - just normal Groovy code - outputs the row id using an embedded string. The SQL example is an invented 'language' where a column is read by id, and then returned to the Java code as the variable text.

So, what about the detail? Well, the approach requires two parts.

Firstly, there needs to be a parser for each language that understands the relevant syntax. This will typically be a variation of the normal parser for a 'real' language like Scala or Ruby. For a new language like SQL or XML, it would be written from scratch. The parser also needs to be able to recognise when the block of code in that language is complete.

Secondly, the parser needs to be able to share variables with the surrounding code. As a basic principle, this can be thought of as a map, where the other language code can both read and write to the map. Of course this requires there to be a mapping between the various type systems - for Groovy this should be easy, other languages might find that more tricky.

So how hard is this to implement? Probably pretty hard. But it does open up lots of possibiilties - wheter for embedded DSLs or larger blocks of code in another language.

Summary

This is an outline of an idea to allow other languages, whether existing or new, to be easily embedded directly in existing code. Any thoughts?