#1145 Obj default constructor accepting a closure

katox Sat 10 Jul 2010

When constructing new objects I tend to use it/'with'-blocks for construction to avoid complex constructor code or having multiple (java-like) constructors.

With nullables or value types I can just use a default constructor and an implicit with block

class A 
{
   Int x
   Obj? data

   new make() { echo("A") }

   static Void main()
   {
     a := A { data = "hello"; x = 2 } // equivalent to A().with({data = "hello" })
     echo("a=$a.data, x=$a.x")
   }
}

however going to non-nullables

class A 
{
   Int x
   Obj data

   new make() { echo("A"); x=2 }

   static Void main()
   {
     a := A { data = "hello"; x = 2 }
     echo("a=$a.data, x=$a.x")
   }
}

the compiler complains about not having all field assigned

Non-nullable field 'data' must be assigned in constructor 'make'

There are basically two options how to prevent this

  1. changing the constructor to accept direct parameters (x, data)
  2. changing the constructor to accept an initializing closure

    class A {

    Int x
    Obj data
    
    new make(|This|? f := null) { echo("A"); f?.call(this) }
    
    static Void main()
    {
      a := A { data = "hello"; x = 2 }
      echo("a=$a.data, x=$a.x")
    }

    }

The second option is much more convenient still not unsafe because I get sys::FieldNotSetErr on instantination (could be a compile error in the future).

With inheritance thrown into that problem the construction using a closure is a bit more flexible. I can decide if f will be executed first (in A constructor) or last (in B constructor) or both but that would lead to nasty side effects (f would be executed twice).

I find myself reluctant to write a parametric constructor for almost any object just because of non-nullable fields so I usually end up with nullable fields and using implicit .with-blocks. A design smell but I am lazy - hell, what could go wrong with a such simple class!

The question is couldn't be new make(|This|? f) { f?.call(this) } the default Obj constructor signature and implementation?

It makes no harm if unused - it is backward-compatible with the original Obj#make. It'd only effectivelly change .with blocks to .it blocks.

There may be performance implications but at first glance it seems that the compiler could just optimize out most of unused code.

What do you think?

go4 Sun 8 Aug 2010

It's good idea.

this need default constructor is new make(|This|? f := null) {f?.call(this) } the constructor can't inherit.

Akcelisto Sun 8 Aug 2010

I support this suggestion.

I tired to write in almost class new make(|This|? f := null) {f?.call(this) }. I use Fantom for fast prototyping and I often change list of fields in class. And I found that closure ctor is more handy then usual ctor.

rfeldman Sun 8 Aug 2010

Sounds excellent! +1

brian Sun 8 Aug 2010

I think this is one of those cases where the developer has to explicitly design his or her class to use an it-block constructor or not.

Although the notion of easier boiler plate for ctors, hash, and equals for "value types" is something that we should eventually do. I just haven't seen a design I really like.

Login or Signup to reply.