While skimming through the Dart language specification tonight, I came across the entry for
typedef
. I am putting out a book on the subject in less than two weeks and I have no idea what typedef
does, so let's rectify.The spec is not terribly illuminating as to purpose, but it definitely applies to functions. I eventually grep through the dart-sdk source code and find a couple of examples. Typedefs seem to be used to constrain callbacks to a specific fingerprint.
Say, I could use that to limit the acceptable data sync callbacks in Hipster MVC. Currently, I allow any method to be injected into
HipsterSync.sync=
:class HipsterSync { // private class variable to hold an application injected sync behavior static Function _injected_sync; // setter for the injected sync behavior static set sync(fn) { _injected_sync = fn; } // ... }To swap out the default data sync layer, I can, for example, use localStorage:
main() { HipsterSync.sync = localSync; // ... } localSync(method, model) { // localStorage awesomeness here }That works just fine, but I have no real way of ensuring (or at least strongly suggesting) that the function assigned via the
HipsterSync.sync
setter is of arity 2, returns a Future, and accepts a String
as the first argument.This is where
typedef
comes in handy.In my
HipsterSync
library, I declare a SyncCallback
typedef:typedef Future<HashMap> SyncCallback(String _method, Dynamic model);As a long time type hater, I am trying to resist the allure of that statement, but it's hard—so very hard to resist this temptation. Especially in my MVC library, that is a big deal. With that line, I declare that any callbacks need to return a
Future
callback—which will, in turn, supply a HashMap
(i.e. the return value of a JSON.parse()
). Moreover, the first argument needs to be a String
(to describe the CRUD operation). I am lax with the second argument because it can be either a HipsterModel
or a HipsterCollection
(I should really create a common interface for the two).With that, I can declare that my
_injected_sync
function needs to be a "SyncCallback":typedef Future<HashMap> SyncCallback(String method, Dynamic model); class HipsterSync { // private class variable to hold an application injected sync behavior static SyncCallback _injected_sync; // setter for the injected sync behavior static set sync(SyncCallback fn) { _injected_sync = fn; // ... }And, if I give my localStorage sync method an arity of 3 instead of
SyncCallback
's 2:main() { HipsterSync.sync = localSync; // ... } localSync(method, model, asdf) { // localStorage awesomeness here }Then I get a nice exception:
That is a type-checked mode exception only, but it is a compile time error, ensuring that I will know that there is a problem immediately. No test suite run, no smoke tests. Nice.
As a long time dynamic programmer, I really want to dislike this, but I cannot find good cause. Even if I can come up with a good excuse to hate, it is not as if Dart is forcing me to use this feature—I didn't even know about it until tonight. Dart is really shaping up to be a gateway drug for strongly typed programming. Dammit.
Day #331
No comments:
Post a Comment