Note: information on this page refers to Ceylon 1.1, not to the current release.
Dynamic typing
Interoperation with a dynamic language like JavaScript poses a special challenge for Ceylon. Since no typing information for dynamically typed values is available at compile time, the compiler can't validate the usual typing rules of the language. Therefore, Ceylon lets us write dynamically typed code where typechecking is performed at runtime.
Partially typed declarations
The keyword dynamic
may be used to declare a function or
value with missing type information. Such a declaration is
called partially typed.
dynamic xmlHttpRequest = ... ;
void handle(dynamic event) { ... }
dynamic findDomNode(String id) { ... }
Note that dynamic
is not itself a type. Rather, it represents
the absence of typing information. Therefore any value is
considered assignable to a dynamic
value or returnable by a
dynamic
function.
Dynamically typed expressions
A dynamically typed expression is an expression that involves
references to program elements for which no typing information
is available. That includes references to values and functions
declared dynamic
, along with things defined in a dynamic
language like JavaScript.
A dynamically typed expression may only occur within a dynamic
block. The dynamic
block serves to suppress certain type checks
that the compiler normally performs.
dynamic xmlHttpRequest;
dynamic {
xmlHttpRequest = XMLHttpRequest();
}
void handle(dynamic event) {
dynamic {
print(event.info);
}
}
Note: you cannot make use of a partially typed declaration
outside of a dynamic
block. The following is not accepted by
the compiler:
void handle(dynamic event) {
print(event.info); //compile error: event has unknown type
}
When a dynamically typed expression is evaluated, certain runtime type checks are performed, which can result in a runtime typing exception.
Interoperating with native JavaScript
The reason Ceylon supports partially typed declarations and dynamically typed expressions is to allow interoperation with JavaScript objects written in JavaScript. The next example illustrates the use of a native JavaScript API. Try it:
dynamic {
dynamic req = XMLHttpRequest();
req.open("HEAD", "http://try.ceylon-lang.org/", true);
req.onreadystatechange = void () {
if (req.readyState==4) {
print(req.getAllResponseHeaders());
}
};
req.send();
}
Dynamic interfaces
But writing dynamically-typed code is a frustrating, tedious, error-prone activity involving lots of debugging and lots of finger-typing, since the IDE can't autocomplete the names of members of a dynamic type, nor even show us the documentation of an object or member when we hover over it.
Therefore, Ceylon makes it possible to write a special sort of interface that captures the typing information that is missing from a JavaScript API. For example:
dynamic IXMLHttpRequest {
shared formal void open(String method, String url, Boolean async);
shared formal variable Anything()? onreadystatechange;
shared formal void send();
shared formal Integer readyState;
shared formal String? getAllResponseHeaders();
//TODO: more operations
}
IXMLHttpRequest newXMLHttpRequest() {
dynamic { return XMLHttpRequest(); }
}
Now we can rewrite the example above, without the use of dynamic
:
IXMLHttpRequest req = newXMLHttpRequest();
req.open("HEAD", "http://try.ceylon-lang.org/", true);
req.onreadystatechange = void () {
if (req.readyState==4) {
print(req.getAllResponseHeaders());
}
};
req.send();
Thus, it's possible to create Ceylon libraries that provide a typesafe view of native JavaScript APIs.
Gotcha!
Note that a dynamic
interface is a convenient fiction! The
Ceylon compiler can't do anything to ensure that the native
JavaScript object you assign to the dynamic
interface type
actually implements the operations that the interface
declares!
So, if you're not careful, you can still get runtime type exceptions!
Dynamic instantiation expressions
Occasionally it's necessary to instantiate a JavaScript Array
or plain JavaScript Object
(which is not the same thing as a
Ceylon Object
!). We may use a special-purpose dynamic
enumeration expression:
dynamic {
dynamic obj = dynamic [ hello="Hello, World"; count=11; ];
print(obj.hello);
print(obj.count);
dynamic arr = dynamic [ 12, 13, 14 ];
print(arr[0]);
print(arr[2]);
}
Note that these expressions are not considered to produce an instance of a Ceylon class.
There's more ...
Well, no, actually, we've finished the tour! Of course, there's still plenty of scope for you to explore Ceylon on your own. You should now know enough to start writing Ceylon code for yourself, and start getting to know the platform modules.
Alternatively, if you want to keep reading you can browse the reference documentation or (if you're sitting comfortably) read the specification.