Monthly Archives: March 2016

Bitter retreat — unfolding aliases

Some time ago I wrote about new way to compare states in DFA. You could take for example two states:

expr -> PLUS . expr { func1 };
expr -> MINUS . expr { func1 }; 

and argue they can be merged in DFA because among other things, they have exactly the same action. Well, I was wrong — sure, the code is shared, the code is the same, but the execution path (flow) can vary. And the difference relies on such basic thing as action parameters — if data associated with PLUS and MINUS are not used the merger is possible. If they are used (most likely) you have to create separate states.

So the correction to the action part is this — if the action is the same and we didn’t read any of the parameters used in the action, we can merge.

Such modification makes merger criterion much weaker and it is not possible to stop aliases from exploding when unfolding them. Thus I had to revert almost completely my entire work. Well, life…

Advertisement
Tagged ,

Towards real OOP — contravariance

I wonder if the boom of dynamic languages is not the effect of the incorrectly stated limits of statically typed languages like Java or C#.

It is pretty basic, but anyway:

interface Contract
  def compute(input String) Object;
end

Interface is a contract indeed — in this example we say that if you refine compute you should be able to tackle with String in input and Object in output. But let’s say we write:

class Worker refines Contract
  refines def compute(input Object) Int •••
end

Did we just violate the contract of the interface? No — we were supposed to produce Object, and Int is Object. We were supposed to cope with String in the input and we do even better — we handle all possible types (nothing is higher in type hierarchy than Object). We fulfilled the contract and added a bonus for those who use Worker type directly.

Well, I cannot speak for other languages, but Skila supports both sides of the refinement.

Tagged , ,

Twin methods

One of the patterns you can see in many languages are paired methods — modify itself, and the other one — create new instance and modify it. For example, it could be the case of any sequence:

// modify itself
def !reverse()
  •••
end

// return the modified version
def reverse() 'Self
  let clone = this.copy();
  clone!reverse();
  return clone;
end

The body of the first method of course varies, but the second one is always the same and there is no point in writing it manually. In Skila you write the first method and you will get the second one automatically.

Tagged ,

Meet the constructors family

Most of the time you will use init constructor which works pretty much as the one seen in C#, Swift, or Python. The memory for object is already allocated, you just have to set the values for the object members.

Next we have field initializers — which you use, but you don’t define explicitly:

class Foo
  let myField Int = 3;
end

That 3 will be moved to field initializer constructor, there is one per each class and it is called automatically just after alloc constructor, which simply allocates memory.

Similarly to field initializer, there is a type initializer — this one can be written explicitly:

class Foo
  static let myField Int = 3;
  static let other Int;

  static private def init()
    this.other = 2;
  end
end

In above code you see only one initialization inside type initializer, but compiler will drag down that 3 there as well.

All the fun is with the remaining two constructors — let’s start with new one, its generated code looks always the same:

class Foo
  •••
  static def () Self // new constructor
    let __this = Self.alloc();
    __this.init();
    return __this;
  end
  def init()
    •••
  end
end

There are some rules concerning new constructor (it has to be static, it has to return Self/!Self type, it cannot have name, it cannot be generic), but because its body is completely repetitive the compiler synthesizes it for every non-partial init constructor there is.

For truly immutable types like Int we can do better though — take a look at copy constructor:

static def (copy Self) Self
  return copy;
end

Instead of init constructor we defined new constructor which returns the original object — this way we prevented creating new object (think: memory allocation) and for free we got new reference.

There is only one constructor left — companion constructor (anonymous factory). How many times when you wrote code in C# you were annoyed you couldn’t write such line ¹:

return new Tuple(3,4);

and you had to painfully instruct C# compiler:

return new Tuple<Int,Int>(3,4);

Skila has special syntax for creating tuple values, but faces the same problem — this is a constructor call and arity of the typename tells which type we are referencing. In the first snippet it would be a type Tuple and the problem is there is no such type. Companion constructors fit just right in:

interface Tuple
  new static def <T0,T1,out C>(_0 T0,_1 T1) Tuple<T0,T1,C>
    where
      C base T0;
      C base T1;

    return (_0,_1);
  end
end

This particular example is an overkill in Skila, it serves only as an illustration. Note the differences, you can place companion constructor in any type, the result type does not have to be Self, the method can be generic and you have to mark such constructor with modifier new, otherwise compiler would assume it is indeed new constructor. In other words you are marking anonymous factory method with new modifier to be treated as new constructor when called — yet, under the hood it is a regular method.


¹ Apparently I am not alone — see: Why can’t the C# constructor infer type? I am curious about history of resolving this issue in C# compiler, because targeting all possible types at the same time adds a lot of complexity.

Tagged

Source code in the wild

This is a big day for me only because it is the first public release of the Skila source code, not because the code is so mature you can only read it in awe. Maybe such day will come too, but until then a fair warning — it is more of a backup of the Skila project, not a proper “release”. Yet, somehow test cases can be executed and the computation is correct, so at least something works.

If you are curious and brave — you are welcome to give it a try.

Tagged

Evolution of a method

My understanding of the difference between getter method and getter property is the latter is the attribute of given type, something that you can just read. The former on the other hand is something you (painfully or not) compute.

It really annoyes me that in C# you have for example the method Count for IEnumerable (granted, as an extension) and you have Count property for such collection as List.

Let’s think in terms of refinement — when you have a method in your base type you can refine it in any derived type, but if at some stage you could guarantee no actual computation takes place you would be sending the wrong signal. Internally you have just a fetch operation, for the outer world you are still struggling with some number crunching. That is why Skila allows to refine a method as a property.

Dead simple:

interface Sequence<out T>
  // here we compute
  base def count() Int •••
end

class !Array<T> extends Sequence<T>
  // here we just read
  extends def count Int •••
end

And to make using properties as pleasant as possible Skila goes the opposite way than Scala — it is possible to call a property like a function but not the other way around:

let seq ~Int = [ 1, 2, 3 ];
_ = seq.count();
// invalid in Skila, valid in Scala
_ = seq.count; 

let coll [Int] = [ 1, 2, 3 ];
// both target the property
_ = coll.count(); // invalid in Scala
_ = coll.count; 
Tagged ,

Tuples — appetite for types

Let’s start with just a pair — as you may know it takes generic types T1, T2, and T3. Wait, a pair with three types? Oh yes, such simple type as tuple has many places where it can be improved. Adding extra type is one of such areas.

This additional type is not completely arbitrary as the rest of the type parameters — it is lowest common ancestor of them. Why do we need common type? For making a tuple a true member of collection family:

let pair Tuple<Int,Int,_> = ( 2, 3 );
let triple = ( 3, 7, 8 );
// reading elements via indexer
let first = triple[0]; 
// iterating over tuple
for elem in triple •••
Tagged ,