Learning Functions for Dart

(This is Part 2 in a series on Dart. See Part 1: Variables and optional types in Dart)

While it's true that Dart is an object oriented, class based, single inheritance language with interfaces, there's nothing forcing you to use classes. If you haven't seen a class in your life, or you don't want to use classes, you will be pleased to see that Dart can function quite nicely with, well, just functions.

Basics

The most simple function I could come up with:

hello() => 'world';

The => e syntax is shorthand for a function body of the form {return e;}.

The above is the same as:

hello() {
  return 'world';
}

Notice how the return type is omitted. Dart is an optionally typed language, so the programmer is free to omit static type declarations.

The above is the same as:

Dynamic hello() {
  return 'world';
}

The Dynamic type is the "unknown" type. From the spec: "If no static type annotation has been provided the type system assumes the declaration has the type Dynamic." You probably won't see real life code actually statically specify the Dynamic type, because the system will insert it if it needs to.

The above can also be written as:

String hello() {
  return 'world';
}

The above example statically declares String as the return type. Compare this version to the very first one-line example.

Parameters

Adding parameters to functions is familiar:

hello(msg) => msg;

Like all Dart code, types are optional for function parameters. For example, the above is the same as the below:

hello(String msg) => msg;

We think types are useful for interfaces, methods, functions, and any place you need to either communicate with your editing tools or larger team. If you haven't seen a type in your life, Dart isn't going to force types on you. However, they are useful for documentation and catching some errors early.

Multiple parameters are supported as well:

hello(msg, to) => msg + ' for ' + to;

We can do better than the overloaded + sign to concatenate strings together. The above is the same as the below:

hello(msg, to) => '${msg} for ${to}';

Ahhh, much better! Not only does the above string interpolation read better (for me, at least) there's a subtle potential performance improvement. Due to Dart's optional typed nature, using the + concatenation means the running program must first determine if it's adding two numbers or two strings. However, using the string interpolation method (eg, above) means the running program can make assumptions.

Of course the above is the same as the below, with types specified:

hello(String msg, String to) => '${msg} for ${to}';

Optional Parameters

Dart has little gems to help you write programs quickly and easily. One of those gems is optional parameters. For example:

hello(msg, to, [from]) => '${from} sent ${msg} to ${to}';

The brackets around the parameter names indicate that parameter is optional.  For example:

hello(msg, to, [from]) => '${from} sent ${msg} to ${to}';

main() => print(hello('world', 'Seth')); // doh! forgot my optional parameter
"null sent world to Seth"

Of course, the null in the above example appears because I never passed in a value for from.

The following example does pass in a value for the last (optional) parameter:

hello(msg, to, [from]) => '${from} sent ${msg} to ${to}';

main() => print(hello('world', 'Seth', 'Bob'));
"Bob sent world to Seth"

Named Parameters

Turns out this gem has a gem of its own. Optional parameters are also named parameters. Check this out:

hello(msg, to, [from]) => '${from} sent ${msg} to ${to}';

main() => print(hello('world', 'Seth', from:'Bob'));

This implies that named parameters can also be reordered when calling the function. For the below example, I added two optional and named parameters:

hello(msg, to, [from, rate]) => '${from} sent ${msg} to ${to} via ${rate}';

main() => print(hello('world', 'Seth', from:'Bob', rate:'First Class'));

Because named parameters can be reordered, the above can be rewritten as:

hello(msg, to, [from, rate]) => '${from} sent ${msg} to ${to} via ${rate}';

main() => print(hello('world', 'Seth', rate:'First Class', from:'Bob'));

This is certainly flexible stuff.

Default Parameters

Yet another bonus of using optional parameters is that they can be given default values. For example:

hello(msg, to, [from, rate='First Class']) => '${from} sent ${msg} to ${to} via ${rate}';

main() => print(hello('world', 'Seth', from:'Bob')); // doh! forgot rate
"Bob sent world to Seth via First Class"

From the spec: "If no default is explicitly specified for an optional parameter, but a default could legally be provided, an implicit default of null is provided."

It should also be noted that default values must be compile time constants. (we'll talk more about what this means in a future blog post) That is, you can't do something like hello(msg, to, [from=new foo(), rate='First Class']).


Inner Functions


Dart understands that there are plenty of developers that don't want to see a class yet still want some composability. One tool that the seasoned web developer will find useful is the "inner function" (what is the proper name here?)  Yes, you can nest functions!


An example of a nested (aka inner) function:


hello(msg, to) {
  importantify(msg) => '!!! ${msg} !!!';

  return '${importantify(msg)} to ${to}';
}

main() => print(hello('world', 'Seth'));
"!!! world !!! to Seth"


I've seen developers use these nested functions as a way to document and organize their function bodies. With proper use of nested functions, you probably don't even need comments inside a function.

Functions are First Class

Dart treats functions as first class objects, further reinforcing the notion that you don't need classes if you don't want them. For example, you can pass a function to a function:


hello(msg, to, wrapper) {
  return '${wrapper(msg)} to ${to}';
}

importantify(msg) => '!!! ${msg} !!!';

main() => print(hello('world', 'Seth', importantify));
"!!! world !!! to Seth"


Summary


Even though Dart is a object oriented, class based, single inheritance language with interfaces, developers can be quite productive and happy using only functions. In fact, if a developer never wants to see a class, they will still find Dart useful and interesting.

Dart supports simple functions with optional and named parameters, with optional default values. Dart functions are first class objects, and can be nested inside other functions.


Epilogue


Dart is not done, and has been launched as a Technology Preview in an effort to solicit early feedback from the community. Please check out this new open source structured web programming language, libraries, VM, and editor and let us know what you think. Learn more at dartlang.org and join the conversation.

The easiest way to try Dart is the browser based Dartboard, allowing you to write and run Dart code without downloading anything.

Popular posts from this blog

Lists and arrays in Dart

Converting Array to List in Scala

Null-aware operators in Dart