Implement Swift's Lazy Variable Syntax for Crystal
There are many situations where we have a property for an object that is expensive to compute. When that property is not used often, it makes sense to
- defer the computation until the first time we need the property, and
- store the value for subsequent references to avoid re-computing it.
Ruby and Crystal developers are familiar with this as “memoization” where the property is access by a method that computes and caches the value.
|
|
Swift has a language feature for memoization using the lazy
keyword to declate dynamic properties for an object.
|
|
This looks so intuitive that I wanted to figure out if I could implement this for my Crystal1 programs. Since Crystal is really good at not compiling unneeded code, and has great macro support2, I started to look into how I could make a lazy
keyword available in Crystal.
The sugar
|
|
The macro
The following lazy
macro converts the lazy prop : T { ... }
declaration into the boilerplate for memoization by generating
- a private member/property
@prop : T?
, - a memoizing getter method
prop
, and - a private method
_lazy_prop_fetch
with the evaluation code.
|
|
Error handling
Crystal’s type inference only goes so far. Declaring lazy sin { Math.sin(@value) }
will not compile, so the lazy
macro catches that otherwise very natural usage to provide a useful error.
For example, the following declaration of twice
…
|
|
… will result in the following compile-time error:
|
|
Availability
I’ve started a sugar.cr shard
in GitHub. This macro is too small to be in its own shard; but I can foresee other similar sugar crystals 😉 that I think will just go into this repo.
-
Crystal is a compiled statically-typed language with Ruby-inspired syntax. ↩︎
-
Macros in Crystal are great, and the documentation here provides plenty of information to really understand and use them. ↩︎