Monday, 6 October 2014

Interesting Devoxx Talk

I came across an interesting talk by Stephen Colebourne on Fantom, from 2011. The talk looks at some features of Fantom, and discusses their novelty in comparison with Scala. I'm not interested in the Scala comparisons, as much as the highlighted parts of Fantom, several of which resonated with me:

1. The pod system.

A pod is the unit of compilation, meaning that, when you run the Fantom compiler, it will compile an entire collection of classes. This is different to, say, Java, which works on a class-by-class basis. The pod is a single unit, which can be distributed and included in a larger system. The pod carries along some meta-data, such as its version, which gives useful information when including pods in a larger system.

2. Types.

The combination of static typing with a dynamic option is one of my favourite Fantom features. Dynamic method calls, checked at runtime, give the feel of a dynamic language, such as Ruby. The type system will infer types for variables, and provide the comfort of static checks before compiling, as with Java.

3. Null safety.

Fantom tackles null safety in two ways. First, the type system allows us to specify whether a field/variable or return value can be null or not. Second, for variables that may hold null, the "safe invoke" and "elvis operator" enable us to write code without some of the usual null tests.

4. Immutability, for concurrency.

Fantom has an actor-based concurrency framework, which relies on immutability of classes. Having this as a fundamental part of the language forces a particular style of writing concurrent programs. A style that promises to remove a certain class of problems in putting together concurrent software.

Whether or not you agree with the premise of Stephen's talk, I found the analysis interesting, and the talk prompted a closer look at why I had chosen to work with Fantom in the first place.

Wednesday, 31 August 2011

Sidestepping Immutability

A requirement of the Actor model in Fantom is that any instance variables are constant and immutable. There is a workaround, which is 'sys::Unsafe': this is a wrapper class, which tells anyone that asks that it is immutable. To use, you simply pass the object you want wrapped in its constructor, and use 'val' (and a cast) to retrieve the value.

Here's a little 'fansh' session to illustrate:

Fantom Shell v1.0.59 ('?' for help)
fansh> a := [1,2,3]
[1, 2, 3]
fansh> a.isImmutable
false
fansh> b := Unsafe (a)
fan.sys.Unsafe@406199
fansh> b.isImmutable
true
fansh> b.val
[1, 2, 3]
fansh> (b.val as List).add (4)
[1, 2, 3, 4]
fansh> b.val
[1, 2, 3, 4]

We can see that 'b' holds a reference to the list, and can even be asked to update the value of the list, but reports to the outside world that it is immutable.

Unsafe Worker

We can use Unsafe to provide the equivalent of mutable instance variables in the Worker class I used in my last post. Below is the complete code for the same example, but now using Unsafe references instead of Actor.locals.

Apart from the cast needed on lines 45 and 50, this now looks more like the SwingWorker example, using instance variables to hold references to the GUI widgets. We can now even refer to the GUI widgets in the 'receive' method, which we could not do using the Actor.locals method: see line 33.

01: using concurrent
02: using fwt
03: 
04: class Calculate
05: {
06:   static Int fib (Int n)
07:   {
08:     if (n < 2)
09:       return 1
10:     else
11:       return (fib (n-2) + fib (n-1))
12:   }
13: }
14: 
15: const class Worker : Actor
16: {
17:   const Unsafe result
18:   const Unsafe button
19: 
20:   new make (Unsafe result, Unsafe button) : super (ActorPool ())
21:   {
22:     this.result = result
23:     this.button = button
24:   }
25: 
26:   override Obj? receive (Obj? msg)
27:   {
28:     // we only care about one message - a number to compute from
29:     n := msg as Int
30:     if (n != null)
31:     {
32:       // we can access our Unsafe variables here!
33:       Desktop.callAsync |->| { (button.val as Button).text = "working!" }
34:       // do the actual computation
35:       startTime := DateTime.now
36:       res := Calculate.fib (n)
37:       // call the 'update' method on the GUI thread
38:       Desktop.callAsync |->| { update(n, res, DateTime.now - startTime) }
39:     }
40:     return null
41:   }
42: 
43:   Void update (Int n, Int res, Duration time)
44:   {
45:     (result.val as Label).with 
46:     {
47:       text = "Fib($n) = $res in $time"
48:       pack
49:     }
50:     (button.val as Button).with
51:     {
52:       text = "compute"
53:       enabled = true
54:       pack
55:     }
56:   }
57: }
58: 
59: class Main
60: {
61:   static Void main ()
62:   {
63:     chooseN := Text { text = "1" }
64:     result := Label ()
65:     button := Button { text = "compute" }
66:     button.onAction.add |Event e|
67:     { // when button clicked
68:       button.with
69:       {
70:         text = "Busy"
71:         enabled = false
72:       }
73:       Worker(Unsafe(result), Unsafe(button)).send (chooseN.text.toInt)
74:     }
75: 
76:     Window
77:     {
78:       title = "Fibonacci"
79:       GridPane // place widgets in a column
80:       {
81:         chooseN,
82:         button,
83:         result,
84:       },
85:     }.open
86:   }
87: }
88: 
89: