**************************************************************************
** title:      Fields
** author:     Brian Frank
** created:    17 Jun 07
** copyright:  Copyright (c) 2007, Brian Frank and Andy Frank
** license:    Licensed under the Academic Free License version 3.0
**************************************************************************

Overview [#overview]
********************
Fields are the mechanism used to manage state in classes.  A single
field encapsulates three concepts:

  1.  **Storage**: a memory location to store a reference to the field's value
  2.  **Getter**: a method to get the field
  3.  **Setter**: a method to set the field

The key concept in Fantom is that *all* fields *automatically* use a getter
and setter method (there are a couple exceptions to this rule we will discuss
below).  In Java fields are simply a storage location.  Most Java programmers
then wrap the field in a getter and setter method resulting in a huge amount
of bloated, boiler plate code.  Then all access is done through a method call
which is not exactly the prettiest syntax for field access.  C# is better in
that properties are first class language constructs.  However management of
storage is still done via a field and all the same boiler plate code is required.
These design patterns used in Java and C# don't capture programmer intent very
cleanly.  The vast majority of these accessors are boiler plate code that don't
actually do anything other than get and set the field.  Plus it requires multiple
language constructs to model one logical construct - this makes documentation
a pain.

Simple Fields [#simple]
***********************
In the simplest case, field declarations look just like Java or C#:

  class Thing
  {
    Int id := 0
    Str? name
  }

In the example above 'id' and 'name' are instance fields on the
class 'Thing'.  We use the ':=' operator to provide an initial value
for the field which is automatically assigned before the constructor
is executed.  If an initial value is not provided, then all fields default
to 'null' with the exception of [value-types]`TypeSystem#valueTypes`.

We access fields using the '.' operator:

  thing := Thing()
  thing.name = "bob"
  echo(thing.id)
  echo(thing.name)

In the code above, a getter or setter is not explicitly specified, so
they are automatically generated by the compiler.  All access to the
field is through the getter and setter method.  So the code 'thing.name'
is really a method call to the 'name' getter method.  This enables you
to later add an explicit accessor without requiring a recompile of all
the client code to your API.

You can also the '?.' operator to safely handle a null target.  See
[safe invokes]`Expressions#safeInvoke`.

Note: if you don't provide a getter or setter on a non-virtual field, then
the compiler will optimize field access internal to your pod.  So inside
your pod, accessor methods are only used if you declare explicit accessors.
Outside your pod, accessor methods are always used for non-const fields.
Abstract and virtual fields always use accessors.

Accessor Methods [#accessors]
*****************************
The syntax for declaring a getter or setter is similar to C# properties:

  class Thing
  {
    Int id := 0
    {
      get { echo("get id"); return &id }
      set { echo("set id"); &id = it }
    }
  }

The accessor section is denoted by a '{}' block after the initial
value assignment (or after the field identifier if no initial value).
You can declare both 'get' and 'set' - or if you just declare one, then
the other is automatically generated.

Inside the setter, the 'it' keyword references the new value being
assigned to the field.  If you need to use 'it' inside a closure, then
assign to a local variable:

  set { newVal := it; 3.times { echo(newVal) }; this.&f = newVal }

The '&' storage operator is used to access the raw storage of the
field (think about like C dereference operator).  The '&' operator is used
when you wish to access the storage location of the field without going
through the accessor method.  You can use the '&' operator in any of your
class's methods, but only the declaring class is permitted direct access
using the '&' operator (it is like storage is always 'private' scoped).
Inside the accessor itself you are required to use the '&' operator (otherwise
you would likely end up with infinite recursion).

Note that ':=' field initialization always sets storage directly without
calling the setter method (see [below]`#virtual` for an exception to
this rule).

Calculated Fields [#calculated]
*******************************
In most cases, the compiler automatically declares storage memory for
your field.  However if the compiler detects that a field's storage
is never accessed then no memory is allocated.  We call these *calculated
fields*.  Calculated fields must declare both an explicit getter and
setter which does not use the '&' operator to access the field.  Note
that assigning an initial value using ':=' implicitly uses the storage
operator and will allocate storage.

Const Fields [#const]
********************************
The keyword 'const' can be applied to your fields to indicate that
the field is immutable:

  class Thing
  {
    new make(Int id) { this.id = id }
    const Int id
  }

In the example above, 'id' is an const instance field.  Const instance
fields can only be set in a constructor.  They work a little different
than Java final fields - you can set them as many times as you like (or
let them default to null), but you can't assign to them outside of the
construction process.

Fantom also allows const instance fields to be set during the construction
process via an [it-block]`Closures#itBlocks`:

  class Thing
  {
    new make(|This f|? f := null) { if (f != null) f(this) }
    const Str name
  }

  t := Thing { name = "Brian" }   // ok
  t { name = "Andy" }             // throws ConstErr

Inside of it-blocks, the check for setting a const field is moved from
compile-time to run-time.  The compiler will allow *any* it-block to set
a const field on 'it'.  However, if an attempt is made to call the
it-block outside of the constructor then 'ConstErr' is thrown.  This
gives you flexibility to build APIs which capture a snippet of code
used to modify an immutable class.

Const instance fields may also configured during deserialization via
the `sys::InStream.readObj` method.

The value of all 'const' fields must be [immutable]`Concurrency#immutability`.
This guarantees that all const fields reference an instance which will
not change state.

Const fields do not use accessor methods.  It is a compile time error to
declare accessors on a const field.  All field access is performed directly
against storage.

Const fields cannot be declared 'abstract' or 'virtual'.  However a const
field can [override]`#overrideMethod` a virtual method with no parameters.
But const fields cannot override fields since that would imply the field
could be set.  Example of using this technique to use a pre-calcualted
string for 'Obj.toStr':

  const class Something
  {
    new make(...) { toStr = "..." }
    override const Str toStr
  }

Static Fields [#static]
***********************
The keyword 'static' is used to create a static class level field:

  class Thing
  {
    const static Int nullId := 0xffff
  }

The field 'nullId' is a const static field - there is only one global
instance on the class itself.  Const static fields can only be set in
[static constructors]`Methods#staticCtor`.  Static fields must be
const - this ensures [thread safety]`Concurrency`.  Static fields
are accessed using the '.' operator on the class itself:

  Thing.nullId

Protection Scope [#protection]
******************************
You can use the normal slot protection scope keywords with a field:
'public', 'protected', 'internal', and 'private'.  The default is 'public'
if a keyword is omitted.

You can narrow the scope of the setter as follows:

  class Thing
  {
    Int id { protected set }
    Str name { internal set { &name = it } }
  }

In the example above the fields 'id' and 'name' are publicly
scoped.  We've narrowed the scope of the 'id' setter to be
protected.  Because there is no method body for the setter of
'id', then the compiler auto generates one.  The declaration
for 'name' shows narrowing the scope *and* declaring an explicit
setter.

Only a protection scope keyword may be used with a setter.  The
getter is always the protection scope of the field itself by
definition.  It is a compile time error to use keywords like
'const', 'virtual', or 'abstract' on an individual getter or
setter.

Readonly Fields [#readonly]
***************************
It is quite common to publically expose a field's getter, but to
hide the setter.  This can be done by narrowing the field's
setter to be 'private' as the following code illustrates:

  class Thing
  {
    Int id { private set }
    internal Int count { private set }
  }

Virtual Fields [#virtual]
*************************
Instance fields may be declared virtual which allows subclasses to
override the getter and setter methods:

  class Thing
  {
    virtual Int id := 0
  }

  class NewThing : Thing
  {
    override Int id
    {
      get { echo("NewThing.id get"); return super.id }
      set { echo("NewThing.id set"); super.id = it }
    }
  }

  class AnotherThing : Thing
  {
    override Int id := 100
  }

In this example 'Thing.id' is virtual which allows subclasses to override
its getter and setter method (in this case 'Thing.id' accessors are auto
generated by the compiler).  The class 'NewThing' overrides the accessors
for 'id' to add some tracing, then calls the superclass accessors.  Note
that the overridden field must specify the 'override' keyword and must be
use the same type (covariance is not supported).

Overridden fields are not given their own storage, rather they should delegate
to their superclass accessors using the 'super' keyword.  In the case
that an override requires its own storage, you should declare another field
to use as storage.

In the example above, the class 'AnotherThing' overrides 'Thing.id' by
providing a different default value.  Since no explicit getter or
setter is specified, the compiler automatically generates accessors.
However the accessors for 'AnotherThing.id' call 'super.id' rather than
access storage directly.  Also note that when an overridden field specifies
an initial value, the field is initialized via its virtual setter
rather than direct storage.

Abstract Fields [#abstract]
***************************
A field may be declared abstract:

  abstract class Thing
  {
    abstract Int id
  }

  class NewThing : Thing
  {
    override Int id := 0
  }

An abstract field is basically just an abstract getter and setter that
a subclass must override.  Abstract fields have no storage and cannot
have an initial value.  Overrides of abstract fields are given storage
accessed via the '&' operator (this is different than overriding a
normal virtual field).

Definite Assignment [#definiteAssignment]
*****************************************
Non-nullable fields must be assigned in all code pathes.  Each constructor
which doesn't chain to 'this' must set each non-nullable field which doesn't
meet one of the following criteria:
  - value type fields don't need to be set (Bool, Int, Float)
  - fields with an initializer don't need to be set
  - abstract or override fields don't need to be set
  - native fields don't need to be set
  - [calcualted]`#calculated` fields don't need to be set

Matching rules apply to static fields which must be set by all code
paths in the static initializer.

As a special case, a constructor which takes an it-block as the last
parameter allows definite assignment to be postponed to runtime.  The
it-block is required to set every non-nullable field or a 'FieldNotSetErr'
is raised at runtime.  Consider this example:

   class Foo
   {
     new make(|This| f) { f(this) }
     Str name
   }

   Foo {}                   // throws FieldNotSetErr
   Foo { name = "Brian" }   // ok

Overriding a Method [#overrideMethod]
*************************************
A field's getter may be used to override a virtual method which has
no parameters:

  abstract class Named
  {
    abstract Str name()
  }

  class Person : Named
  {
    override Str name
  }

In the example above 'Named.name' is a virtual method that returns a 'Str'.
The class 'Person' overrides the 'Named.name' method using a field.  The
override of 'Named.name' maps to the getter of 'Person.name'.  Note that
the field must specify the 'override' keyword and must have a matching type.
Field overrides of a method may use [covariance]`Inheritance#covariance` - the
field may be declared using a more specific type than the method return
type.

This design pattern is a handy technique to use in your Fantom code.

Mixins [#mixins]
****************
A mixin can only declare const static fields and abstract fields.  Mixins
with abstract fields are basically declaring a getter/setter signature.
When implementing a mixin with abstract fields, it is the subclass's
responsibility to provide an implementation of the field.  Field access
of mixins uses the standard '.' dot operator too.

Native Fields [#native]
************************
Native fields are implemented in an alternate language which is "native"
for each target platform.  Native fields are typically written in Java
for the Java VM and C# for the .NET CLR.  Native fields use the 'native'
keyword and follow similiar rules to absract fields - no storage or
accessors.  The infrastructure for supporting native fields is discussed
in the `Natives` chapter.