Lazy initialization of statics

2,844 views
Skip to first unread message

Gilad Bracha

unread,
Feb 28, 2012, 7:22:37 PM2/28/12
to General Dart Discussion
In Dart static variables may only be initialized with compile-time constants (hereafter referred to as just constants in the interest of brevity).  Static variables include all top-level variables as well as static variables of classes. The motivation is our desire to avoid costly initialization (and attendant slowness) at program startup. However, the requirement to use constants is quite restrictive.

We plan on relaxing the restrictions on initialization of statics while still avoiding high latency at startup by making the initialization lazy. We can evaluate the initializer at first use rather than at library or class load time.

We are aware that the interaction of laziness and imperative programming is problematic, because results are timing dependent. Currently this is a non-issue because the initializers evaluate constants, which are timing-independent.

Nevertheless, lifting the restriction that statics be initialized is a clear win, and has have no semantic effect on currently legal programs. Code that relies on side-effects in initialization could behave in surprising ways, but order dependent initialization is a bad idea and should be avoided in any case.

Below are the relevant spec changes. For speakers of English, the executive summary is:


You will be able to use any expression (not just constant expressions) to initialize a static variable (including top level variables). The initialization will be carried out upon the first invocation of the getter of that variable (aka first read). If the initialization throws an exception, the variable will be set to null, and the exception will be propagated.



Specification Changes


As usual, changes highlighted in yellow.

Variables

Variables are storage locations in memory.  

variableDeclaration:
     declaredIdentifier (',' identifier)*
   ;

initializedVariableDeclaration:
     declaredIdentifier ('=' expression)? (',' initializedIdentifier)*
   ;

initializedIdentifierList:
     initializedIdentifier (',' initializedIdentifier)*
   ;

initializedIdentifier:
     identifier ('=' expression)?
   ;


declaredIdentifier:
     finalVarOrType identifier
   ;



finalVarOrType:
     final type?
   |
var
   | type
   ;




A variable that has not been initialized has the initial value null.
A final variable is a variable whose declaration includes the modifier final. A final variable can only be assigned once, when it is initialized, or a compile-time error occurs.

A static variable is a variable that is not associated with a particular instance, but rather with an entire library or class.  

Static variable declarations are initialized lazily. The first time a static variable v is read, it is set to the result of evaluating its initializer. The precise rules are given in sections 7.7 and 10.28.

The lazy semantics are given because we do not want a language where one tends to define expensive initialization computations, causing long application startup times. This is especially crucial for Dart, which is designed for coding client applications.


If a variable declaration does not explicitly specify a type, the type of the declared variable(s) is Dynamic, the unknown type.

A top-level variable is implicitly static. It is a compile-time error to preface a top level variable declaration with the built-in identifier static.  It is a compile-time error if a top level variable is initialized with an expression that is not a compile-time constant.


Static Variables


Static variables are variables whose declarations are immediately contained within a class declaration and that are declared static. The static variables of a class C are those static variables declared by C.

A static variable declaration of one of the forms static T v;, static T v = e; or static final T v = e; always induces an implicit static getter function with signature

static T get v()

whose invocation evaluates as described below.

A static variable declaration of one of the forms static var  v;, static var  v = e; or static final v = e; always induces an implicit static getter function with signature

static get v()

whose invocation evaluates as described below.

A non-final static variable declaration of the form static T v; or the form static T v = e; always induces an implicit static setter function with signature

static void set v(T x)

whose execution sets the value of v to the incoming argument x.

A non-final static variable declaration of the form static var v; or the form static var v = e; always induces an implicit static setter function with signature

static set v(x)

whose execution sets the value of v to the incoming argument x.

Evaluation of static variable getters



Let d be the declaration of a static variable v. The implicit getter method of v executes as follows:
  • If d is of one of the forms static var v = e; , static T v = e; , static final v = e; or static final T v = e; and no value has yet been stored into v then the initializer expression e is evaluated. If the evaluation succeeded yielding an object o, let r = o, otherwise let r = null. In any case, r is stored into v. The result of executing the getter is r. Otherwise
  • The result of executing the getter method is the value stored in v.  



Identifier Reference


An identifier expression consists of a single identifier; it provides access to an object via an unqualified name.

identifier:
      IDENTIFIER
  | BUILT_IN_IDENTIFIER
  ;


IDENTIFIER_NO_DOLLAR:
     IDENTIFIER_START_NO_DOLLAR IDENTIFIER_PART_NO_DOLLAR*
   ;

IDENTIFIER:
     IDENTIFIER_START IDENTIFIER_PART*
   ;

   ;

BUILT_IN_IDENTIFIER:
     abstract
   | assert
   | call
   | Dynamic
   | factory
   | get
   | implements
   | import
   | interface
   | library
   | negate
   | operator
   | set
   | source
   | static
   | typedef
   ;


IDENTIFIER_START:
     IDENTIFIER_START_NO_DOLLAR
   | '$'
   ;

IDENTIFIER_START_NO_DOLLAR:
     LETTER
   | '_'
   ;

IDENTIFIER_PART_NO_DOLLAR:
     IDENTIFIER_START_NO_DOLLAR
   | DIGIT
   ;



IDENTIFIER_PART:
     IDENTIFIER_START
   | DIGIT
   ;



qualified:
     identifier ('.' identifier)?
   ;



A built-in identifier is one of the identifiers produced by the production BUILT_IN_IDENTIFIER. It is a compile-time error if a built-in identifier is used as the declared name of a class, interface, type variable or type alias. It is a compile-time error to use a built-in identifier other than   Dynamic as a type annotation. It is a static warning if a built-in identifier is used as the name of a user-defined declaration, be it a variable, function, type or label, with the exception of user defined operators named negate or call.

Built-in identifiers are identifiers that are used as keywords in Dart, but are not reserved words in Javascript. To minimize incompatibilities when porting Javascript code to Dart, we do not make these into reserved words. However, a built-in identifier may not be used to name a class or type.  In other words, they are treated as reserved words when used as types. This eliminates many confusing situations without causing compatibility problems.

Evaluation of an identifier expression e of the form id proceeds as follows:
Let d be the innermost declaration in the enclosing lexical scope whose name is id. It is a compile-time error if d is a class, interface, type alias or type variable. If no such declaration exists in the lexical scope, let d be the declaration of the inherited member named id if it exists.
  • If d is a library variable then:
    • If d is of one of the forms var v = ei; , T var v = ei; , final v = ei; or final T v = ei; and no value has yet been stored into v then the initializer expression ei is evaluated. If the evaluation succeeded yielding an object o, let r = o, otherwise let r = null. In any case, r is stored into v. The value of e is r. Otherwise
    • e evaluates to the current binding of id.  This case also applies if d is a library function declaration, as these are equivalent to function-valued variable declarations.
  • If d is a local variable or formal parameter then e evaluates to the current binding of id.  This case also applies if d is a local function declaration, as these are equivalent to function-valued variable declarations.
  • If d is a static method, then e evaluates to the function defined by d.
  • If d is the declaration of a static variable or static getter declared in class C, then e is equivalent to the getter invocation C.id.
  • If d is the declaration of a top level getter, then e is equivalent to the getter invocation id.
  • Otherwise e is equivalent to the property extraction  this.id.



--
Cheers, Gilad

Bob Nystrom

unread,
Feb 28, 2012, 7:51:58 PM2/28/12
to Gilad Bracha, General Dart Discussion
This is awesome. Now can we get support for non-const initializers for instance fields too? :)

- bob

Gilad Bracha

unread,
Feb 28, 2012, 7:58:33 PM2/28/12
to Bob Nystrom, General Dart Discussion
On Tue, Feb 28, 2012 at 4:51 PM, Bob Nystrom <rnys...@google.com> wrote:
This is awesome. Now can we get support for non-const initializers for instance fields too? :)

That is more complicated. Rome was not built in a day.



--
Cheers, Gilad

Rémi Forax

unread,
Feb 28, 2012, 8:09:52 PM2/28/12
to mi...@dartlang.org
On 02/29/2012 01:58 AM, Gilad Bracha wrote:
>
>
> On Tue, Feb 28, 2012 at 4:51 PM, Bob Nystrom <rnys...@google.com
> <mailto:rnys...@google.com>> wrote:
>
> This is awesome. Now can we get support for non-const initializers
> for /instance/ fields too? :)

>
>
> That is more complicated. Rome was not built in a day.
>
>
> - bob
>

no more lazy singleton in Dart, it's shocking :)

There is one thing I didn't get: why static initializer has to be constant ?
Knowing that a constant constructor can do side effect, I don't see the
interest.

R�mi


>
>
> On Tue, Feb 28, 2012 at 4:22 PM, Gilad Bracha <gbr...@google.com
> <mailto:gbr...@google.com>> wrote:
>
> In Dart static variables may only be initialized with
> compile-time constants (hereafter referred to as just

> constantsin the interest of brevity). Static variables


> include all top-level variables as well as static variables of
> classes. The motivation is our desire to avoid costly
> initialization (and attendant slowness) at program startup.
> However, the requirement to use constants is quite restrictive.
>
> We plan on relaxing the restrictions on initialization of
> statics while still avoiding high latency at startup by making
> the initialization lazy. We can evaluate the initializer at
> first use rather than at library or class load time.
>
> We are aware that the interaction of laziness and imperative
> programming is problematic, because results are timing
> dependent. Currently this is a non-issue because the
> initializers evaluate constants, which are timing-independent.
>
> Nevertheless, lifting the restriction that statics be
> initialized is a clear win, and has have no semantic effect on
> currently legal programs. Code that relies on side-effects in
> initialization could behave in surprising ways, but order
> dependent initialization is a bad idea and should be avoided
> in any case.
>
> Below are the relevant spec changes. For speakers of English,

> *the executive summary* is:
>
>
> /You will be able to use any expression (not just constant


> expressions) to initialize a static variable (including top
> level variables). The initialization will be carried out upon
> the first invocation of the getter of that variable (aka first
> read). If the initialization throws an exception, the variable

> will be set to null, and the exceptionwill be propagated./


>
>
>
> Specification Changes
>
>
> As usual, changes highlighted in yellow.
>
>
> Variables
>
> Variables are storage locations in memory.
>
> variableDeclaration:
> declaredIdentifier

> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=id.2xe95ykmes0u>(','
> identifier)*
> ;
>
> initializedVariableDeclaration:
> declaredIdentifier
> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=id.2xe95ykmes0u>('='
> expression)? (','initializedIdentifier
> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=id.cjgl4bau1n1l>)*
> ;
>
> initializedIdentifierList:
> initializedIdentifier (','initializedIdentifier
> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=id.cjgl4bau1n1l>)*


> ;
>
> initializedIdentifier:
> identifier ('=' expression)?
> ;
>
> declaredIdentifier:
> finalVarOrType identifier
> ;
>
>
> finalVarOrType:
> finaltype?
> | var
> | type
> ;
>
>
>
>
> A variable that has not been initialized has the initial value
> null.

> A final variableis a variable whose declaration includes the
> modifier final.A final variable can only be assigned once,


> when it is initialized, or a compile-time error occurs.
>

> A static variableis a variable that is not associated with a


> particular instance, but rather with an entire library or class.
>
> Static variable declarations are initialized lazily. The first

> time a static variable vis read, it is set to the result of


> evaluating its initializer. The precise rules are given in
> sections 7.7

> <https://docs.google.com/a/google.com/document/d/1xnLrrLiR-hJ9vyKFl2ouZEhPNxjvRT9EBHp04nEVr2g/edit#bookmark=id.2ay2xojrdai1>and
> 10.28
> <https://docs.google.com/a/google.com/document/d/1xnLrrLiR-hJ9vyKFl2ouZEhPNxjvRT9EBHp04nEVr2g/edit#bookmark=id.h1r7vmqzkcij>.


>
>
> The lazy semantics are given because we do not want a language
> where one tends to define expensive initialization
> computations, causing long application startup times. This is
> especially crucial for Dart, which is designed for coding
> client applications.
>
>
> If a variable declaration does not explicitly specify a type,
> the type of the declared variable(s) is Dynamic, the unknown
> type

> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=kix.5kehb35oxqwh>.


>
> A top-level variable is implicitly static. It is a
> compile-time error to preface a top level variable declaration
> with the built-in identifier static. It is a compile-time
> error if a top level variable is initialized with an
> expression that is not acompile-time constant

> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=id.hzs87hup8wb>.
>
>
> Static Variables
>
> **
> Static variablesarevariables
> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=kix.6b1cgvgf1cyq>whose


> declarations are immediately contained within a class

> declaration and that are declared static.The static variables
> of a class Care those static variables declared by C.


>
> A static variable declaration of one of the forms staticT v;,

> staticT v = e; or static finalT v = e; always induces an
> implicit staticgetter function
> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=kix.wlocpej6rvqa>with
> signature
>
> staticT get v()


>
> whose invocation evaluates as described below

> <https://docs.google.com/a/google.com/document/d/1xnLrrLiR-hJ9vyKFl2ouZEhPNxjvRT9EBHp04nEVr2g/edit#bookmark=id.4vi4vmhjmrew>.


>
> A static variable declaration of one of the forms static

> var v;, static var v = e; or static finalv = e; always


> induces an implicit staticgetter function

> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=kix.wlocpej6rvqa>with
> signature
>
> staticget v()


>
> whose invocation evaluates as described below

> <https://docs.google.com/a/google.com/document/d/1xnLrrLiR-hJ9vyKFl2ouZEhPNxjvRT9EBHp04nEVr2g/edit#bookmark=id.4vi4vmhjmrew>.


>
> A non-final static variable declaration of the form staticT

> v;or the form staticT v = e; always induces an implicit
> staticsetter function
> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=kix.i4xvz9z9edz>with
> signature
>
> static voidset v(T x)
>
> whose execution sets the value of vto the incoming argument x.


>
> A non-final static variable declaration of the form static var

> v;or the form static varv = e; always induces an implicit
> staticsetter function
> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=kix.i4xvz9z9edz>with
> signature
>
> static set v(x)
>
> whose execution sets the value of vto the incoming argument x.


>
>
> Evaluation of static variable getters
>
>
>

> Let dbe the declaration of a static variable v.The implicit


> getter method of vexecutes as follows:
>

> * If d is of one of the forms static varv = e; , static T v
> = e; , static finalv = e;or static finalT v = e;and no


> value has yet been stored into vthen the initializer

> expression eis evaluated. If the evaluation succeeded


> yielding an object o, let r = o,otherwise let r = null.In

> any case, ris stored into v.The result of executing the
> getter is r. Otherwise
> * The result of executing the getter method is the value


> stored in v.
>
>
>
>
> Identifier Reference
>

> **
> An identifier expressionconsists of a single identifier; it

> the exception of user defined operators named negateor call.


>
> Built-in identifiers are identifiers that are used as keywords
> in Dart, but are not reserved words in Javascript. To minimize
> incompatibilities when porting Javascript code to Dart, we do
> not make these into reserved words. However, a built-in
> identifier may not be used to name a class or type. In other
> words, they are treated as reserved words when used as types.
> This eliminates many confusing situations without causing
> compatibility problems.
>
> Evaluation of an identifier expression eof the form idproceeds
> as follows:

> Let dbe the innermost declaration in the enclosing lexical
> scope whose name is id. It is a compile-time error if dis a


> class, interface, type alias or type variable. If no such

> declaration exists in the lexical scope, let dbe the


> declaration of the inherited member named idif it exists.
>

> * If dis a library variable then:
> o If d is of one of the forms varv = ei; , T varv = ei;
> , finalv = ei;or finalT v = ei;and no value has yet


> been stored into vthen the initializer expression eiis

> evaluated. If the evaluation succeeded yielding an
> object o, let r = o,otherwise let r = null.In any

> case, ris stored into v.The value of eis r. Otherwise
> o eevaluates to the current binding of id.This case also
> applies if dis a library function declaration, as


> these are equivalent to function-valued variable
> declarations.

> * If dis a local variable or formal parameter then
> eevaluates to the current binding of id.This case also
> applies if dis a local function declaration, as these are


> equivalent to function-valued variable declarations.

> * If dis a static method, then eevaluates to the function
> defined by d.
> * If dis the declaration of a static variable or static


> getter declared in class C,then eis equivalent to
> thegetter invocation

> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=id.lharm2td3qkb>C.id.
> * If dis the declaration of a top level getter, then eis
> equivalent to thegetter invocation
> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=id.lharm2td3qkb>id.
> * Otherwise eis equivalent to theproperty extraction
> <https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBgat4gYgH0mSFGuyrJpbcT5bTw/edit?hl=en_US#bookmark=id.hu9dfddw6bhy>this.id.

Sean Eagan

unread,
Feb 28, 2012, 9:45:32 PM2/28/12
to Gilad Bracha, Bob Nystrom, General Dart Discussion
At the risk of making a complete fool of myself... couldn't it work in exactly the same way, that is with instance fields inducing instance getters whose first invocation (for a given instance) before any value previously being stored, evaluates the initializer expression, stores the value (or null on failure), and returns the value.  Instance field initializers would have access to "this" as well, since the getter they desugar to does.
--
Sean Eagan

Florian Loitsch

unread,
Feb 29, 2012, 4:15:19 AM2/29/12
to Rémi Forax, mi...@dartlang.org
On Wed, Feb 29, 2012 at 02:09, Rémi Forax <fo...@univ-mlv.fr> wrote:
On 02/29/2012 01:58 AM, Gilad Bracha wrote:


On Tue, Feb 28, 2012 at 4:51 PM, Bob Nystrom <rnys...@google.com <mailto:rnys...@google.com>> wrote:

   This is awesome. Now can we get support for non-const initializers
   for /instance/ fields too? :)



That is more complicated. Rome was not built in a day.


   - bob


no more lazy singleton in Dart, it's shocking :)

There is one thing I didn't get: why static initializer has to be constant ?
Knowing that a constant constructor can do side effect, I don't see the interest.
Maybe I am misunderstanding, but a constant constructor can *not* do any side effects.
// florian 

Rémi



--
Give a man a fire and he's warm for the whole day,
but set fire to him and he's warm for the rest of his life. - Terry Pratchett

Rémi Forax

unread,
Feb 29, 2012, 5:34:13 AM2/29/12
to Florian Loitsch, mi...@dartlang.org
On 02/29/2012 10:15 AM, Florian Loitsch wrote:

> On Wed, Feb 29, 2012 at 02:09, R�mi Forax <fo...@univ-mlv.fr
> <mailto:fo...@univ-mlv.fr>> wrote:
>
> On 02/29/2012 01:58 AM, Gilad Bracha wrote:
>
>
>
> On Tue, Feb 28, 2012 at 4:51 PM, Bob Nystrom
> <rnys...@google.com <mailto:rnys...@google.com>
> <mailto:rnys...@google.com <mailto:rnys...@google.com>>> wrote:
>
> This is awesome. Now can we get support for non-const
> initializers
> for /instance/ fields too? :)
>
>
>
> That is more complicated. Rome was not built in a day.
>
>
> - bob
>
>
> no more lazy singleton in Dart, it's shocking :)
>
> There is one thing I didn't get: why static initializer has to be
> constant ?
> Knowing that a constant constructor can do side effect, I don't
> see the interest.
>
> Maybe I am misunderstanding, but a constant constructor can *not* do
> any side effects.
> // florian

Hi Florian,
you can call a function in the initializer list that will do a side effect.

foo() {
print("side effect");
}

class A {
final a;
const A():a=foo();
}

A a = const A();

R�mi

Nicolas Geoffray

unread,
Feb 29, 2012, 5:41:55 AM2/29/12
to Rémi Forax, Florian Loitsch, mi...@dartlang.org
Hi Remi,

On Wed, Feb 29, 2012 at 11:34 AM, Rémi Forax <fo...@univ-mlv.fr> wrote:
On 02/29/2012 10:15 AM, Florian Loitsch wrote:

This is not allowed. If you see this behavior, it's a bug.

Cheers,
Nicolas
 
Rémi


Rémi Forax

unread,
Feb 29, 2012, 7:04:29 AM2/29/12
to Nicolas Geoffray, Florian Loitsch, mi...@dartlang.org
On 02/29/2012 11:41 AM, Nicolas Geoffray wrote:
> Hi Remi,
>
> On Wed, Feb 29, 2012 at 11:34 AM, R�mi Forax <fo...@univ-mlv.fr
> <mailto:fo...@univ-mlv.fr>> wrote:
>
> On 02/29/2012 10:15 AM, Florian Loitsch wrote:
>
> On Wed, Feb 29, 2012 at 02:09, R�mi Forax <fo...@univ-mlv.fr
> <mailto:fo...@univ-mlv.fr> <mailto:fo...@univ-mlv.fr

Hi Nicolas,
I've just read the latest spec, you're right, the implementations (VM
and frog) are just too permissive.

cheers,
R�mi

William Hesse

unread,
Feb 29, 2012, 7:19:38 AM2/29/12
to Rémi Forax, Nicolas Geoffray, Florian Loitsch, mi...@dartlang.org
Does this change still allow implementers to perform constant
initialization eagerly?
It looks as if the semantics don't change, if the initialization value
is a constant.

Would this then just introduce a hidden aspect of implementations,
that statics initialized with a constant can be more efficient than
statics initialized with a non-constant? Or are we expecting code to
be compiled after the static is initialized, and that that code will
not do the initialization check?

What are the use cases where this change would be useful? I see
mainly the case of a singleton, with complex initialization of the
singleton.

On Wed, Feb 29, 2012 at 1:04 PM, Rémi Forax <fo...@univ-mlv.fr> wrote:
> On 02/29/2012 11:41 AM, Nicolas Geoffray wrote:
>>
>> Hi Remi,
>>
>>

>> On Wed, Feb 29, 2012 at 11:34 AM, Rémi Forax <fo...@univ-mlv.fr


>> <mailto:fo...@univ-mlv.fr>> wrote:
>>
>>    On 02/29/2012 10:15 AM, Florian Loitsch wrote:
>>

>>        On Wed, Feb 29, 2012 at 02:09, Rémi Forax <fo...@univ-mlv.fr

> Rémi
>

--
William Hesse
Software Engineer
whe...@google.com

Google Denmark ApS
Frederiksborggade 20B, 1 sal
1360 København K
Denmark
CVR nr. 28 86 69 84

If you received this communication by mistake, please don't forward it
to anyone else (it may contain confidential or privileged
information), please erase all copies of it, including all
attachments, and please let the sender know it went to the wrong
person. Thanks.

Lasse R.H. Nielsen

unread,
Feb 29, 2012, 8:25:35 AM2/29/12
to Gilad Bracha, General Dart Discussion
How does this handle (direclty or mutually) recursive initializations?

E.g.
var foo = () { return foo + 1; }();
or
var foo = bar + 1;
var bar = foo + 1;

We have requirements against cyclic dependencies in compile time
constants (10.1), but I don't see anything here. Rather, the getter
will just end up calling itself again, because it reads the field
before it's written, causing an infinite recursion.

Maybe we need to have a unique marker value that we can write to the
field while evaluating the initializer, and if we ever read that
value, we'll throw some cyclic-dependency-error exception instead (and
set the field to null on the way out). Or can we rely on the
stack-overflow exception to do that?

/L 'String operator+(String x)! operator==(x)! operator ()(x,y)! operator-(x)!'
--
Lasse R.H. Nielsen
l...@google.com
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K -
Denmark - CVR nr. 28 86 69 84

Sean Eagan

unread,
Feb 29, 2012, 9:07:53 AM2/29/12
to Lasse R.H. Nielsen, Gilad Bracha, General Dart Discussion
What is the reasoning behind suppressing exceptions (i.e. just returning null) on evaluating the initializer ?
--
Sean Eagan

Rémi Forax

unread,
Feb 29, 2012, 10:15:30 AM2/29/12
to mi...@dartlang.org
On 02/29/2012 03:07 PM, Sean Eagan wrote:
> What is the reasoning behind suppressing exceptions (i.e. just
> returning null) on evaluating the initializer ?

As far as I understand, only subsequent access to the constant variable
will returning null.

But I prefer the semantics of Java static block, if an exception occurs,
all subsequent calls will return the same exception.

R�mi

>
> On Wed, Feb 29, 2012 at 7:25 AM, Lasse R.H. Nielsen <l...@chromium.org
> <mailto:l...@chromium.org>> wrote:
>
> How does this handle (direclty or mutually) recursive initializations?
>
> E.g.
> var foo = () { return foo + 1; }();
> or
> var foo = bar + 1;
> var bar = foo + 1;
>
> We have requirements against cyclic dependencies in compile time
> constants (10.1), but I don't see anything here. Rather, the getter
> will just end up calling itself again, because it reads the field
> before it's written, causing an infinite recursion.
>
> Maybe we need to have a unique marker value that we can write to the
> field while evaluating the initializer, and if we ever read that
> value, we'll throw some cyclic-dependency-error exception instead (and
> set the field to null on the way out). Or can we rely on the
> stack-overflow exception to do that?
>
> /L 'String operator+(String x)! operator==(x)! operator ()(x,y)!
> operator-(x)!'
> --
> Lasse R.H. Nielsen

> l...@google.com <mailto:l...@google.com>


> 'Faith without judgement merely degrades the spirit divine'

> Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 K�benhavn K -

Sam McCall

unread,
Feb 29, 2012, 11:05:25 AM2/29/12
to Rémi Forax, mi...@dartlang.org
On Wed, Feb 29, 2012 at 4:15 PM, Rémi Forax <fo...@univ-mlv.fr> wrote:
On 02/29/2012 03:07 PM, Sean Eagan wrote:
What is the reasoning behind suppressing exceptions (i.e. just returning null) on evaluating the initializer ?

As far as I understand, only subsequent access to the constant variable
will returning null.

But I prefer the semantics of Java static block, if an exception occurs,
all subsequent calls will return the same exception.

Really? 

public class Test {
  private static class Foo {
    private static final String name = whoAmI();
    private static final String whoAmI() {
      throw new RuntimeException("I don't know!");
    }
  }

  public static void main(String[] args) {
    System.out.println("First");
    try {
      System.out.println(Foo.name);
    } catch (Throwable e) {
      e.printStackTrace();
    }
    System.out.println("Second");
    try {
      System.out.println(Foo.name);
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }
}

Output:
First
java.lang.ExceptionInInitializerError
at Test.main(Test.java:10)
Caused by: java.lang.RuntimeException: I don't know!
at Test$Foo.whoAmI(Test.java:4)
at Test$Foo.<clinit>(Test.java:3)
... 1 more
Second
java.lang.NoClassDefFoundError: Could not initialize class Test$Foo
at Test.main(Test.java:16)


Gilad Bracha

unread,
Feb 29, 2012, 12:34:09 PM2/29/12
to William Hesse, Rémi Forax, Nicolas Geoffray, Florian Loitsch, mi...@dartlang.org
On Wed, Feb 29, 2012 at 4:19 AM, William Hesse <whe...@google.com> wrote:
Does this change still allow implementers to perform constant
initialization eagerly?

Yes. If it is a compile-time constant, you cannot observe the timing, so you can choose to do it eagerly.

What are the use cases where this change would be useful?

I think the set of useful cases is pretty much unbounded. Anywhere where you want to initialize a static with something that is not a Dart constant.  And Dart constants are pretty restrictive.

--
Cheers, Gilad

Gilad Bracha

unread,
Feb 29, 2012, 12:35:00 PM2/29/12
to Lasse R.H. Nielsen, General Dart Discussion
You are always free to write an infinite recursion. 
--
Cheers, Gilad

Bob Nystrom

unread,
Feb 29, 2012, 2:16:42 PM2/29/12
to Gilad Bracha, William Hesse, Rémi Forax, Nicolas Geoffray, Florian Loitsch, mi...@dartlang.org
If you hunt through the repo, you can find a number of examples of code like:

Foo get foo() {
  if (_foo == null) _foo = new Foo();
  return _foo;
}
Foo _foo;

With this change, all of those can just be:

final foo = new Foo();

- bob

dave ford

unread,
Mar 27, 2012, 12:38:45 PM3/27/12
to General Dart Discussion
+1 on lazy initializers for instance vars

Zuking
On Feb 28, 5:51 pm, Bob Nystrom <rnyst...@google.com> wrote:
> This is awesome. Now can we get support for non-const initializers for *
> instance* fields too? :)
>
> - bob
>
>
>
> On Tue, Feb 28, 2012 at 4:22 PM, Gilad Bracha <gbra...@google.com> wrote:
> > In Dart static variables may only be initialized with compile-time
> > constants (hereafter referred to as just constants in the interest of
> > brevity).  Static variables include all top-level variables as well as
> > static variables of classes. The motivation is our desire to avoid costly
> > initialization (and attendant slowness) at program startup. However, the
> > requirement to use constants is quite restrictive.
>
> > We plan on relaxing the restrictions on initialization of statics while
> > still avoiding high latency at startup by making the initialization lazy.
> > We can evaluate the initializer at first use rather than at library or
> > class load time.
>
> > We are aware that the interaction of laziness and imperative programming
> > is problematic, because results are timing dependent. Currently this is a
> > non-issue because the initializers evaluate constants, which are
> > timing-independent.
>
> > Nevertheless, lifting the restriction that statics be initialized is a
> > clear win, and has have no semantic effect on currently legal programs.
> > Code that relies on side-effects in initialization could behave in
> > surprising ways, but order dependent initialization is a bad idea and
> > should be avoided in any case.
>
> > Below are the relevant spec changes. For speakers of English, *the
> > executive summary* is:
>
> > *You will be able to use any expression (not just constant expressions)
> > to initialize a static variable (including top level variables). The
> > initialization will be carried out upon the first invocation of the getter
> > of that variable (aka first read). If the initialization throws an
> > exception, the variable will be set to null, and the exception will be
> > propagated.*
>
> > Specification Changes
> > As usual, changes highlighted in yellow.
>
> > VariablesVariables are storage locations in memory.
>
> > variableDeclaration:
> >       declaredIdentifier<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>(',' identifier)*
> >    ;
>
> > initializedVariableDeclaration:
> >       declaredIdentifier<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>('=' expression)? (','
> > initializedIdentifier<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>
> > )*
> >    ;
>
> > initializedIdentifierList:
> >       initializedIdentifier (',' initializedIdentifier<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>
> > )*
> >    ;
>
> > initializedIdentifier:
> >       identifier ('=' expression)?
> >    ;
>
> > declaredIdentifier:
> >       finalVarOrType identifier
> >    ;
>
> > finalVarOrType:
> >       final type?
> >    | var
> >    | type
> >    ;
>
> > A variable that has not been initialized has the initial value null.
> > A final variable is a variable whose declaration includes the modifier
> > final. A final variable can only be assigned once, when it is
> > initialized, or a compile-time error occurs.
>
> > A static variable is a variable that is not associated with a particular
> > instance, but rather with an entire library or class.
>
> > Static variable declarations are initialized lazily. The first time a
> > static variable v is read, it is set to the result of evaluating its
> > initializer. The precise rules are given in sections 7.7<https://docs.google.com/a/google.com/document/d/1xnLrrLiR-hJ9vyKFl2ou...>and
> > 10.28<https://docs.google.com/a/google.com/document/d/1xnLrrLiR-hJ9vyKFl2ou...>.
>
> > The lazy semantics are given because we do not want a language where one
> > tends to define expensive initialization computations, causing long
> > application startup times. This is especially crucial for Dart, which is
> > designed for coding client applications.
>
> > If a variable declaration does not explicitly specify a type, the type of
> > the declared variable(s) is Dynamic, the unknown type<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>
> > .
>
> > A top-level variable is implicitly static. It is a compile-time error to
> > preface a top level variable declaration with the built-in identifier
> > static.  It is a compile-time error if a top level variable is
> > initialized with an expression that is not a compile-time constant<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>
> > .
>
> > Static Variables**
> > Static variables are variables<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>whose declarations are immediately contained within a class declaration and
> > that are declared static. The static variables of a class C are those
> > static variables declared by C.
>
> > A static variable declaration of one of the forms static T v;, static T v
> > = e; or static final T v = e; always induces an implicit static getter
> > function<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>with signature
>
> > static T get v()
>
> > whose invocation evaluates as described below<https://docs.google.com/a/google.com/document/d/1xnLrrLiR-hJ9vyKFl2ou...>
> > .
>
> > A static variable declaration of one of the forms static var  v;, static
> > var  v = e; or static final v = e; always induces an implicit static getter
> > function<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>with signature
>
> > static get v()
>
> > whose invocation evaluates as described below<https://docs.google.com/a/google.com/document/d/1xnLrrLiR-hJ9vyKFl2ou...>
> > .
>
> > A non-final static variable declaration of the form static T v; or the
> > form static T v = e; always induces an implicit static setter function<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>with signature
>
> > static void set v(T x)
>
> > whose execution sets the value of v to the incoming argument x.
>
> > A non-final static variable declaration of the form static var v; or the
> > form static var v = e; always induces an implicit static setter function<https://docs.google.com/a/google.com/document/d/1YblLwWryUj0VZBJ2xBga...>with signature
>
> > static set v(x)
>
> > whose execution sets the value of v to the incoming argument x.
>
> > Evaluation of static variable getters
>
> > Let d be the declaration of a static variable v. The implicit getter
> > method of v executes as follows:
>
> >    - If d is of one of the forms static var v = e; , static T v = e; , static
> >    final v = e; or static final T v = e; and no value has yet been stored
> >    into v then the initializer expression e is evaluated. If the
> >    evaluation succeeded yielding an object o, let r = o, otherwise let r
> >    = null. In any case, r is stored into v. The result of executing the
> >    getter is r. Otherwise
> >    - The result of executing the getter method is the value stored in v.
>
> > Identifier Reference**
> ...
>
> read more »

John Tobey

unread,
Mar 29, 2012, 2:17:10 PM3/29/12
to General Dart Discussion
On Feb 29, 3:16 pm, Bob Nystrom <rnyst...@google.com> wrote:
Sorry, but this fails to convince me. If you want a shorthand for
this pattern, why not introduce the "||=" operator, whose semantics
should be obvious.

Foo get foo() => _foo ||= new Foo();

I just find it SOOO surprising that "final foo = new Foo()" does not
execute "new Foo()". Can anyone cite a precedent in another
language? Where is the conservatism that kept Dart from being a fully
cool, functional language when you need it?

Gilad Bracha

unread,
Mar 29, 2012, 2:32:32 PM3/29/12
to John Tobey, General Dart Discussion
On Thu, Mar 29, 2012 at 11:17 AM, John Tobey <john....@gmail.com> wrote:


Sorry, but this fails to convince me.

Sorry to hear that, but, you can't please everyone. 
 
 If you want a shorthand for
this pattern, why not introduce the "||=" operator, whose semantics
should be obvious.

Foo get foo() => _foo ||= new Foo();

The obvious semantics would be compound assignment:

_foo =  _foo = _foo || new Foo(); 

So that particular syntax is not viable. In any case, we never want to allow eager initialization of statics, because we don't want to get slow initialization.  So we if we wanted a special notation for lazy assignment , we'd have to ban normal assignment syntax for statics. That is certainly surprising. 

We're *very* happy with the solution we have for this particular issue. 

--
Cheers, Gilad

John Tobey

unread,
Mar 29, 2012, 2:45:21 PM3/29/12
to General Dart Discussion


On Mar 29, 2:32 pm, Gilad Bracha <gbra...@google.com> wrote:
> On Thu, Mar 29, 2012 at 11:17 AM, John Tobey <john.to...@gmail.com> wrote:
> >  If you want a shorthand for
> > this pattern, why not introduce the "||=" operator, whose semantics
> > should be obvious.
>
> > Foo get foo() => _foo ||= new Foo();
>
> The obvious semantics would be compound assignment:
>
> _foo =  _foo = _foo || new Foo();
>
> So that particular syntax is not viable.

Huh? "return a+=4" means "a=a+4; return a". "return _foo ||= new
Foo()" means "_foo = _foo || new Foo(); return _foo". Where's the
problem?

> In any case, we never want to
> allow eager initialization of statics, because we don't want to get slow
> initialization.  So we if we wanted a special notation for lazy assignment
> , we'd have to ban normal assignment syntax for statics. That is certainly
> surprising.
>
> We're *very* happy with the solution we have for this particular issue.

I'm glad you are happy, but with all due respect, you are much more
experienced at this than Dart's target user. Any evidence of
needlessly slow pages where this would help? Any experience with
similar semantics in another language?

Gilad Bracha

unread,
Mar 29, 2012, 2:54:17 PM3/29/12
to John Tobey, General Dart Discussion
On Thu, Mar 29, 2012 at 11:45 AM, John Tobey <john....@gmail.com> wrote:


On Mar 29, 2:32 pm, Gilad Bracha <gbra...@google.com> wrote:
> On Thu, Mar 29, 2012 at 11:17 AM, John Tobey <john.to...@gmail.com> wrote:
> >  If you want a shorthand for
> > this pattern, why not introduce the "||=" operator, whose semantics
> > should be obvious.
>
> > Foo get foo() => _foo ||= new Foo();
>
> The obvious semantics would be compound assignment:
>
> _foo =  _foo = _foo || new Foo();
>
> So that particular syntax is not viable.

Huh?  "return a+=4" means "a=a+4; return a".  "return _foo ||= new
Foo()" means "_foo = _foo || new Foo(); return _foo".  Where's the
problem?


We want lazy initialization, not logical boolean assignment.  The assignment above is not lazy, and produces a boolean value.  It would never assign new Foo() to _foo. It would however cuase new Foo() to executed eagerly, which isnot what we want.
 

> In any case, we never want to
> allow eager initialization of statics, because we don't want to get slow
> initialization.  So we if we wanted a special notation for lazy assignment
> , we'd have to ban normal assignment syntax for statics. That is certainly
> surprising.
>
> We're *very* happy with the solution we have for this particular issue.

I'm glad you are happy, but with all due respect, you are much more
experienced at this than Dart's target user.  Any evidence of
needlessly slow pages where this would help?  

I have a modest amount of experience with Java, where slow startup crippled the platform as a client side solution.  My colleagues likewise. That experience wil have to do.



--
Cheers, Gilad

John Tobey

unread,
Mar 29, 2012, 3:19:07 PM3/29/12
to General Dart Discussion
On Mar 29, 2:54 pm, Gilad Bracha <gbra...@google.com> wrote:
> On Thu, Mar 29, 2012 at 11:45 AM, John Tobey <john.to...@gmail.com> wrote:
> > Huh?  "return a+=4" means "a=a+4; return a".  "return _foo ||= new
> > Foo()" means "_foo = _foo || new Foo(); return _foo".  Where's the
> > problem?
>
> We want lazy initialization, not logical boolean assignment.  The
> assignment above is not lazy, and produces a boolean value.  It would never
> assign new Foo() to _foo. It would however cuase new Foo() to executed
> eagerly, which isnot what we want.

I want to let this horse die, but you keep surprising me. Apparently
try.dartlang.org is not up to date and gives "2" instead of "true" for
"2 || 3". My wish for ES-like boolean operators is a separate issue.

> > > In any case, we never want to
> > > allow eager initialization of statics, because we don't want to get slow
> > > initialization.  So we if we wanted a special notation for lazy assignment,
> > > we'd have to ban normal assignment syntax for statics. That is certainly
> > > surprising.

That is, if nobody can think of an unsurprising interpretation of
normal assignment.

> I have a modest amount of experience with Java, where slow startup crippled
> the platform as a client side solution.  My colleagues likewise. That
> experience wil have to do.

Java sure did start slowly. But the one to beat is JavaScript, not
Java. There is lower-hanging fruit, IMHO, than the verbosity of
getters that initialize conditionally.

Good luck with your project.

Bob Nystrom

unread,
Mar 29, 2012, 4:19:37 PM3/29/12
to John Tobey, General Dart Discussion
On Thu, Mar 29, 2012 at 11:17 AM, John Tobey <john....@gmail.com> wrote:
On Feb 29, 3:16 pm, Bob Nystrom <rnyst...@google.com> wrote:
> On Wed, Feb 29, 2012 at 9:34 AM, Gilad Bracha <gbra...@google.com> wrote:
>
> > On Wed, Feb 29, 2012 at 4:19 AM, William Hesse <whe...@google.com> wrote:
> >> What are the use cases where this change would be useful?
>
> > I think the set of useful cases is pretty much unbounded. Anywhere where
> > you want to initialize a static with something that is not a Dart constant.
> >  And Dart constants are pretty restrictive.
>
> If you hunt through the repo, you can find a number of examples of code
> like:
>
> Foo get foo() {
>   if (_foo == null) _foo = new Foo();
>   return _foo;}
>
> Foo _foo;
>
> With this change, all of those can just be:
>
> final foo = new Foo();

Sorry, but this fails to convince me.  If you want a shorthand for
this pattern, why not introduce the "||=" operator, whose semantics
should be obvious.

The problem with ||= is that it confuses "falsiness" and "not initialized". How would you handle:

bool get foo() => _foo ||= someVerySlowCalculationThatMayReturnFalse();

?
 

Foo get foo() => _foo ||= new Foo();

I just find it SOOO surprising that "final foo = new Foo()" does not
execute "new Foo()".

It will indeed execute new Foo(). In fact, it guarantees that it will do so before you ever access foo, so it is pretty much as eager as you could want it to be.

Keep in mind that the top level of a Dart program is not a sequence of imperative statements. It's a collection of definitions and order is not meaningful. If that wasn't the case, then things like recursive top-level functions or types that refer to each other wouldn't work (unless you get into JS-style hoisting but let's not go there). This program:

foo() { print(i); }
final i = 123;

Does not mean "define a function foo that prints i then define i". It means "define a function foo that prints i and i". Dart isn't like JS or Python or other dynamic languages in this respect.

Given that, stating that static initializers execute eagerly in the order that they appear in the program text would be really strange. Nothing else in the language works that way. Statements are executed in order, but definitions aren't statements.
 
Can anyone cite a precedent in another language?

Java and C#? I believe static fields are lazy-initialized in both of those.

This really isn't as crazy as you seem to think it is. All this means is that your statics will be initialized before you use them, but won't be initialized if you don't use them. That means when your app imports some_huge_library, you won't burn startup time while it does a bunch of pointless static initialization for features your aren't using. That sounds like a good thing to me.

The only way this can burn you is if your static initializers have visible side effects. If you do that, I claim that you are living in sin anyway, and get what you deserve. :)
 
Where is the conservatism that kept Dart from being a fully cool, functional language when you need it?

We're saving it up so that we can spend it all later adding goto and getting rid of closures. ;)

- bob

Sam McCall

unread,
Mar 29, 2012, 4:42:09 PM3/29/12
to Bob Nystrom, John Tobey, General Dart Discussion
On Thu, Mar 29, 2012 at 10:19 PM, Bob Nystrom <rnys...@google.com> wrote:
 
Can anyone cite a precedent in another language?

Java and C#? I believe static fields are lazy-initialized in both of those.

You meant lazy-ish, right? :-)

'import' in java indeed doesn't execute any initializers, which is awesome.

But instead they're executed at class initialization time, and class initialization is typically a side-effect of some piece of unrelated code. So it's the worst of both worlds - eager in the sense that it can happen "before you're ready", and lazy in the sense that if you want to make sure that it's happened you have to jump through hoops.
And the spec for class initialization is complicated, and the behaviour is unintuitive and causes non-local bugs, and you can't cache as much state as you should be able to...

Looking at the spec, I'm pretty sure "don't initialize libraries like Java" was an explicit design goal :-)

John Tobey

unread,
Mar 29, 2012, 4:44:34 PM3/29/12
to General Dart Discussion
On Mar 29, 4:19 pm, Bob Nystrom <rnyst...@google.com> wrote:
> The problem with ||= is that it confuses "falsiness" and "not initialized".

Minor issue. Perl went and created a new operator //= that checks for
definedness. But the point is to create syntactic sugar for the
common case, and 99% of the time, the laborious construction produces
an object that converts to true.

> > I just find it SOOO surprising that "final foo = new Foo()" does not
> > execute "new Foo()".
>
> It will indeed execute new Foo(). In fact, it guarantees that it will do so
> before you ever access foo, so it is pretty much as eager as you could want
> it to be.

Not if I don't access foo.

> foo() { print(i); }
> final i = 123;
>
> Does *not* mean "define a function foo that prints i *then* define i". It
> means "define a function foo that prints i *and* i". Dart isn't like JS or
> Python or other dynamic languages in this respect.

Indeed, I had not yet perceived this paradigm shift. I will
reevaluate in the new light. Thanks!

> > Can anyone cite a precedent in another language?
>
> Java and C#? I believe static fields are lazy-initialized in both of those.

I don't know C#, but I'm pretty sure Java runs all of a given class's
initializers at "class load" time irrespective of access to particular
fields. However, you are right in that Java loads classes on first
use of a member, and that is kind of similar to what we have here.

> The *only* way this can burn you is if your static initializers have
> visible side effects. If you do that, I claim that you are living in sin
> anyway, and get what you deserve. :)

:) Not another preachy language, please!

> We're saving it up so that we can spend it all later adding goto and
> getting rid of closures. ;)

Oooh! awesome! Can I have COME FROM too? ;)

Rémi Forax

unread,
Mar 30, 2012, 3:24:40 AM3/30/12
to mi...@dartlang.org

Don't forget that Dart is not the same language as Java,
Java doesn't have isolates so static variables can be accessed concurrently,
that's why Java does all static initializations at class init because
the code has to hold a lock.

R�mi


Reply all
Reply to author
Forward
0 new messages