Functions
Functions in Dart are a lot like their JavaScript equivalents. They are first-class objects, they are closures, and they can have optional parameters, although the semantics of the optional arguments are somewhat different. There are, however, some significant differences in the syntax. To begin with, there is a short form, similar to that found in CoffeeScript, inherited from some functional languages.
For example, this form is most useful when you are using higher-order functions, for example as something to pass to a map or fold method on a collection:
add(a, b) => a+b;
You might provide some extra checking by explicitly stating the types of the arguments and the return value:
num add(num a, num b) => a+b;
Here we see a limitation with the Dart type system. The return type annotation is superfluous—the language specification states that the result of adding two numbers is also a number—but if you remove it and try to assign the result to, for example, a String, then you do not get a compile-time error. This is particularly strange because the checker actually does perform this inference. Consider this fragment:
String add(num a, num b) => a+b;
This will produce an error, highlighting the a+b and telling you that a num cannot be assigned to a String. The checker therefore knows that the result of this is a num, but it refuses to make use of this information. Solving the type inference problem in the general case is difficult—just ask anyone who has worked on an Ocaml implementation—but a language like Dart has several advantages.
In particular, because the type system does not affect things like dispatch, it can put up with a best-effort type inference. If it can only prove that a function returns a number in all cases, rather than proving that it returns an integer in the specific case where it is called with two integers, that is perfectly acceptable and still likely to provide helpful results.
It's worth remembering that this is a limitation of the current implementation of Dart, and not of the underlying language. One nice thing about type systems that don't affect the run-time semantics of a program is that you can ship a fairly simple checker initially and then progressively improve it.
With a language like C++ or Ocaml, if you don't get the type checker completely right the first time, it won't compile all your code. With Dart, newer versions can add progressively more and more checks, without altering the program semantics.
In the next article, I'll continue this look at Dart and, in particular, its differences from JavaScript.