[Grace-core] Lazy initialisation and circular definitions
James Noble
kjx at ecs.vuw.ac.nz
Wed Nov 19 01:33:35 PST 2014
Scala and Eiffel have support for lazy initialisation.
Eiffel has "once" routines that look like parameterless methods, but that are only called once
and that cache the return value.
Scala has "lazy vals" that look like constant definitions, but where they are initialised when first
accessed and again cache the value of their initialiser.
Newspeak (above 0.08) has simultaneous slot declarations that bind the slot to a "promise"
(a proxy for its initialiser). These promises are forced when all slots are bound, so simultaneous
slots can be recursive, but the laziness does not persist after the object's construction.
Idris has "mutual" declarations - I think these are somewhat similar.
Marco Servetto's placeholders (ECOOP 2013) are similar to Newspeak's design, except placeholders
are opaque until they are all resolved (and include types to prevent errors) while Newspeak's proxies
allow calls between simultaneous slots provided they are well founded. Racket's "shared"
is roughly similar to placeholders but rather more complex (http://docs.racket-lang.org/reference/shared.html).
These facilities can be used to support some recursive definitions, as well as
lazy initialisation more generally. How could we support this in Grace?
some options:
- basically nick the Eiffel design: have as special kind of "def" called a "once"
this adds a whole new declaration for something that won't be used very often
- nick the Scala design so we could write a "lazy" annotation.
def x : Number is lazy = y + z
this is a bit slightly lexically complex than Scala for the same power.
but this annotation seems to change the semantics of the language;
We don't yet have a metamodel for annotations, but supporting this would
require a model where all initialisers were nominally packed into closures
before annotations were processed.
- implement explicit "virtual proxies" that would take a block, and evaluate it on first access
and either transparently forward (delegate) all requests, or transparently replace themselves
def x : Number = lazy { y + z }
this doesn't need an annotation, or to change semantics, although the syntax is a bit nastier overall. T
If we had "does not understand" and delegation we could implement this reflexively.
To me it seems a bit less "built in" than the other approaches, and "lazy" can be used in
more general computations as a delay with an implicit force.
- implement some kind of placeholder/ simultaneous/ shared/ mutual definitions as a separate
scoped declaration construct
As an idea about how complex this can all get, see e.g.
notes on implementing Scala's lazy val. Another reason
to keep this out of the language core, perhaps?
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
'
More information about the Grace-core
mailing list