Experimentation Stage

Constructors

In an Object Oriented language there is the innate need to instantiate objects. The instantiation occurs in two phases. First the memory needed to hold the object information is allocated and pointed at by a reference. Then this memory space is filled with initialization information. Only after these two phases are complete the object really exists and can be used by other objects.

The allocation phase can be manual like in C , Python and Swift or automatic like in C# or Java. Lense opts for an automatic allocation.

The initialization part is more delicate because the object must always exist in a valid state. For simple objects whose state is simply a bunch of properties that are initialized with default values, this process is straightforward. However when calculations or other computations are needed this presents a problem.

So there needs to be a contract between the machinery responsible for allocating and initializing the object and the object class. Some functions must be runned by this machinery in order to guarantee the object is in a valid state before it can be release in the wild.

This special function invoked by the machinery is called Constructor. Thus, the constructor is often an initialization method and it does not really constructs the object. Because the object already exists when the constructor is called (created in the previous phase of the instantiation process) the constructor has access to it to ensure the properties in the object are correctly set to valid values, however it should not call any polymorphic methods on the object.

As an example, in Java, we would write something like:

// java
public class Fraction {

	private int numerator;
	private int denominator;

	public Fraction (int numerator, int denominator){
		if (denominator == 0){
			trows new IllegalArgumentException("Denominator cannot be zero");
		}
		this.numerator = numerator;
		this.denominator = denominator;
	}
}

// and can be used like

Fraction aThird = new Fraction(1,3);

Pay attention the constructor is vary similar to a method but as no return type and its name is the same as the class.

Inside the constructor, the this keyword refers to the already allocated object. So, at this point, the object already exists and it is of the class Fraction.The constructor has no means to return an object of another class because the constructor has no return.

The constructor , being a special kind of method can throw exceptions , however it is considered bad practice to do so[needs references].

Constructors are usefully to guarantee the object state is correctly initialized but have some limitations, so in practice you would prefer to use a static factory method , like so:

// java
public class Fraction {

	private int numerator;
	private int denominator;

	public static Fraction of(int numerator, int denominator){
		if (denominator == 0){
			throws new IllegalArgumentException("Denominator cannot be zero");
		}
		return new Fraction(numerator, denominator);
	}

	private Fraction (int numerator, int denominator){
		this.numerator = numerator;
		this.denominator = denominator;
	}
}

// but now the object is created like

Fraction third = Fraction.of(1,3);

The validation of parameters is now moved to a method, so exceptions are allowed. The constructor is private so only the class can invoke it, the rest of the world would have to call the of static method.

The problem with static methods is that they are not inherited so, the static factory method pattern makes sense only for object not intended to be inherited from. Nevertheless considered a good practice e several API nowadays use this technique to hide the constructor and provide a more fluent instantiation.

A great thing about this pattern is that it encasuplates the use of new so the class designed can change the parameters of the constructor at will without interfering with the call site.

People often criticize java for being to verbose. The numerator and denominator names appear 6 times.

Types of constructors

Other languages come up with new flavours of constructors to try to reduce the problems with constructors. However, because constructors are essentially linked with the concept of object instantiation and state validity they cannot be removed from the languages. Some type of constructor must exist.

Primary Constructor

This is special kind of constructors that only initializes properties in the object. It is functionally equivalent to the private constructor in our second example above. Because it can only set properties, languages try to come up with special (shorter) syntax.

In Scala, Ceylon and Kotlin, for instance, the primary constructor parameters are declared immediately after the class name and extra code can be added in the class body without any other special delimiters:

// scala, kotlin, ceylon
class Fraction (Integer numerator, Integer denominator) {
    // other code goes where
}

This constructors immediately informs the compiler there must be a numerator and a denominator field in the class and the values of the parameters should be directly assign to those fields. This really reduces the boilerplate but leaves the validation of state problem orphan.

Scala resolves this by means of companion objects that have methods that act like a static factory methods calling the constructor only after validating the parameters are correct. Other special constructors are possible (called auxiliary constructors) but they action is limited. In scala constructors are pretty much meant only for field initialization, other computations are made in methods on objects.

In languages with primary constructors implemented like this trade one boilerplate for asymmetry: instead of having a delimited boilerplate inside a method like syntax, they provide a shorter syntax for the primary constructor making the other constructors syntax ad hoc and not symmetric, even to allow code in the class body and require the programmer to conform to special rules for execution of code inside the class body and inside the auxiliary constructors. They remove the boilerplate of having a constructors that sets the fields but have no dedicated place for "advanced" construction code and the class structure. If simple property bags in what you need this strategy really pays off, but it shows some cracks for more complex types that need to isolate construction a little better.

Dart goes another way.

// dart
class Fraction  {
	Integer numerator;
	Integer denominator;

   Fraction (this.numerator, this.denominator)
}

The syntax is different, more in line with the C syntax like Java, but the intent is the same: reducing boilerplate, but maintaining the tradiconal way constructors are represented. However we are obliged to repeat the class name simply by the convention rule constructors are created this way. Traditionally the C family languages do not use a keyword for the constructor because it was introduces in the language after de initial design and so create a keyword could conflict with existing names in existing code. So the designed made it so the code for the construtor was not valid code in the previous versions.Them, by historic and similarity reason more modern languages simply copied the syntax like Java and Dart. The "same name" rule is not always the case. Scala uses def this() and some languages use new in an attempt to not introduce a dedicated keyword.

In Lense the primary constructor is written :

public class Fraction {

   constructor (public let numerator : Integer, public let denominator : Natural);

}

// and invoke as

let aThird = new Fraction(1, 3);

A constructor without a body means the parameters represent properties with the same name and visiblity. Parameters marked with let will produce read-only properties. Parameters marked with var will produce read-write properties.

There is no boilerplate and there is no repetition of the class’s name and the keyword clearly states that the instruction is a constructor.

Named Constructors

All is fine when the class only needs one constructor. But more times, than not, people would realize an object can be created by different forms. Design can argument this other forms should be handled by factory object and the class itself as only a set of parameters. While this can obviously accomplished is not practical.

If we intend to have a Color type that can be created from RGB or HSL values the two algorithms are different and one or both require calculations before we can set the object private fields. On the other hand we need some practical way of distinguishing between them. Here the static method factory comes handy because it provides a name to the construction form. So in java we could write

// java
Color a = Color.fromRGB(1.0 , 1.0 , 1.0);
Color b = Color.fromHSL(60 , 0.5 , 0.5);

However there is no new keyword being used. Dart provides the same sintax but using new:

// dart
Color a = new Color.fromRGB(1.0 , 1.0 , 1.0);
Color b = new Color.fromHSL(60 , 0.5 , 0.5);

In Dart you can provide named constructors like

// dart
 class Color {

    Color.fromRGB(red, greee, blue){
         // code goes here
    }

    Color.fromHSL(hue, saturation, lightness){
         // code goes here
    }

}

Its a little odd to have dots in the name of the constructor , but at least is consistent with the traditional constructor syntax. In Lense because we have the constructor key word we simply write the same as:

class Color {

    constructor fromRGB( red: Rational, green: Rational, blue : Rational){
         // code goes here
    }

    constructor fromHSL( hue: Rational, saturation: Rational, lightness: Rational){
         // code goes here
    }

}

and invoke them in the same way

let a = new Color.fromRGB(1.0 , 1.0 , 1.0);
let b = new Color.fromHSL(60 , 0.5 , 0.5);

Note the similarity with the anonymous constructor invocation.

The named constructors must, at some point, directly or indirectly, invoke the primary constructor. So the final code should be something like

public class Color {

	private constructor( private let rgb : Natural);

    public constructor fromRGB( red : Rational,  green: Rational, blue: Rational){
         	var rgb : Natural = red * 255;
			rgb = (rgb << 8) + green * 255;
			rgb = (rgb << 8) + blue * 255;
			return new Color(rgb);
    }

    public constructor fromHSL(hue: Rational, saturation: Rational, lightness: Rational){
         // code goes here to calculate red, green and blue from the parameters , then call the fromRGB constructor
         Rational red = ...
         Rational green = ...
         Rational blue = ...
         return new Color.fromRGB(reg,green,blue);
    }

}

Notice how the new keyword is used to call the other constructors. In fact constructors in Lense act as factory methods and can return any object that could be assigned to the class.

Factory Constructor

Constructors in Lense are real factory methods and can create and return any instance that could be assigned to the class. This means constructors can control the number of instances being created and choose to create specific sub types. For instances the Array constructor is :

public class Array<T> implements EditableSequence<T> {

        constructor filled( size: Natural, value : T){
        	if (T is Int32){
        	    return new Int32Array(size, value);
        	} else if (T is Int64){
        	    return new Int64Array(size, value);
        	} else if (T is Byte){
        	    return new ByteArray(size, value);
        	} else {
        	    return new ObjectArray<T>(size, value);
        	}
        }

		constructor ofAbsent<T?>( size : Natural){
        	return new Array.filled<T?>(size, none);
        }

		// other methods
}

The Natural constructor is equivalent to:

public class Natural extends Whole {

	object cache {
		let values = new Array.absent<Natural>(10);
	}

    public constructor (value: Natural){
       	if (value >= 0 && value < cache.values.size -1){
       		let cached = cache.valueslink:or(value[value];
       		cache.values[cached] = cached;
       		return cached;
       	}
       	return other;
    }

    public constructor parse ( value : String){
    	if (value.startsWith("-")){
    		throw new ParseException("Value cannot be negative");
    	}
    	if (value.contains(".")){
    		throw new ParseException("Value cannot be decimal");
    	}

    	var power = 0;
    	var value = 0;
    	for(char in value.replaceAll("_",""){

    		let digit = char.toDigit();

    		if (digit == none){
    			throw new ParseException(char ++ "is not a digit.");
    		}
    		value += digit * 10 ^^ power;

    		power++;
    	}

    	return new Natural(value);
    }
}

It uses and object to hold the cache data. If the given String is not valid the constructor throws a ParseException. This is valid because a constructors is like a factory, however the compiler will only allow the throw clause on a named constructor.

<a name="conversion"></a> === Conversion Constructor

A conversion constructor is used to obtain the state of the object from another object of a different type. For instance:

let k : Integer = 23;

Because all whole literals are parser by the compiler as Natural`s, 23 is really a `Natural. On the other hand, because Natural`s are not `Integer`s the assignment would not be valid. Before a compilation error is risen, the compiler tries to find a constructor in the class Integer that is marked as `implicit and has a single parameter of type Natural.

public class Integer extends Whole {

	implicit constructor ( other : Natural){
		return new BigInt(other.toString()); // this is not the real code, just an example.
	}
}

If it exists, the compiler changes the assignment to:

let k : Integer = new Integer(23);

The implicit keyword is necessary because not every constructor with a single parameter is meant to be a conversion constructor. The List<T> class (used above) has a constructor that receives a Natural to set the array size,but that, without the implicit keyword would mean that:

let list : List<Integer> = 3;

was really

let list : List<Integer> = new List<Integer>(3);

The instruction would be (wrongly) trying to assign the number 3 to the list but the compiler would try to promote the value. This would not be a very coherent form to create arrays because can be confused with:

let list : List<Integer> = [3];

The programmer may have forgotten to surround the value with brackets.

Also, this other example could be made to be valid code using an implicit conversion constructor:

let address : Uri = "http://www.google.com"

// equivalent to

let address : Uri = new Uri("http://www.google.com");

Implicit constructors, like primary constructors, are not recommended for object creation that can throw exceptions (under consideration). For a parsing operation, or other, that possibly could go wrong, is not suited to a conversion constructor. It is recommended that a constructor based on a string be a named constructor like parse(String). Only named constructors can throw exceptions.

As we can see from the above examples, the conversion constructor is a simple way to promote values of one class to another but only if it is guaranteed that conversion will never fail.

As a limitation of conversion constructors the process only works if the class on the left side of the assignment accepts the instances of the class on the right side as a valid argument. This means than , if A is convertible to B and B to C , A is not convertible directly to C.

Constructors Enhancement (Under Consideration)

If the original designer of the left side class did not added the conversion constructor for some other class we can add one latter by creating an enhancement, like so:

public enhancement AddNaturalConvertionConstrutorToString extends String { // enhances String

	public implicit constructor fromNumber( n : Number){ // creates a string from a Number
	       return n.asString();
	}
}

With this enhancement in scope we can write:

let  s : String = 8; // not supported without the enhancement
// or
let  s : String = new String.fromNumber(8); // not supported without the enhancement

This is very powerful feature of enhancements and can easily be abused, so please design enhancements with care.