Tag Archives: constraints

Type unions and intersections

This is something I wanted to do from the first days of Skila, however the engine in Skila-1 was such a patchwork that it died before it was possible. Skila-3 finally has both kinds of sets:

let y *Submarine & *Plane = ••• 
let x *Submarine | *Plane = •••

The first line declares an intersection — we are making here some really super vehicle which can fly, dive and speedUp, because it has all the members of given element types.

On the other hand, the second line builds somewhat limited vehicle using a type union — we can only speedUp, because it has only common members, which are present in each of the given element type.

Is it practical in real life or is it just a fancy academia stuff? The former, the lack of set types in C# is killing me — I can mitigate the problem a little when I am getting data:

void loan<V>(V vehicle) 
  where V : Submarine, Plane

which gives me an intersection, but I am toasted when it comes to the output:

V create<V>() 
  where V : Submarine, Plane

It breaks the responsibility rule, because caller of create has to specify concrete type and inside create we have no chance to really create it (except for trivial case with default constructor).

And I have to cope with it all the time when writing wrappers for devices — I take them specifying their capabilities as constraints but I am unable to specify the capabilities of what I create (unless I am willing to create ton of interfaces — TelescopeWithThis, TelescopeWithThat).

Advertisement
Tagged , , ,

Prototypes and “has” constraint

I am really glad I went with interpreter — it allows me to check ideas much faster than before, when I was playing with PHP transpiler.

I added support for prototypes (as in Go) and an option for regular interfaces to turn them into prototypes as well — so you can type substitution matching or structural matching:

prototype PHost
{
  string say();
}

interface IHost
{
  string say();
}

struct Host : IHost
{
  public override string say() => "hello";
}

let h1 *IHost = new Host();
h1.say();
let h2 *PHost = new Host();
h2.say();

The first declaration and call work because the types are matched as in C# — Host is derived from IHost and substitution is possible. In the second case it is also possible but not because Host is derived from PHost (it is not), but because all the methods PHost requests are defined in Host.

I am wondering about adding option to limit type implementation (class or struct) substitution. Say you have class “Parent” and derived from it class “Child”. You could pass *Child for *Child or *Parent for *Parent, but not *Child for *Parent. For full substitution you would have to go with interfaces/prototypes and this in turn would promote using them.

Supporting the notion of the duck typing already paid off — it was fairly easy to add has constraints for templates:

void welcome<T>(actor *T)
  where T has string say();
{
  actor.say();
}

When there is any has constraint present, compiler turns internally the type parameter into a prototype and the rest is already written.

In the example above I pass the pointer simply because I didn‘t yet write templates properly and they don’t work with values. Maybe next weeek…

Tagged , ,

Double-edged constraints with a twist

One of the types in Skila library is generic Sequence (the counterpart of IEnumerable interface in C#). Assuming method concat is not supposed to create exactly the same type as the original one (as in Array for Array, List for List, and so on) but simply some Sequence how its signature should look like?

In C# it is defined as an extension method:

public static IEnumerable<TSource> Concat<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second
)

Which is almost fine until you have to play with such code:

animals = cats.Select(it => (Animal)it).Concat(dogs);

Obviously it is an obstacle and those are not welcome in Skila — here is our signature:

interface Sequence<out T>
  // ~ stands for Sequence
  def concat<C>(seq ~C) ~C
    where C base T;
    ...
  end
end

Unlike C#, Skila has two-way constraints — you can say some type parameter A has to inherit from B (A is B), or A has to be inherited by B (A base B). As for concat it is enough to write:

animals = cats +++ dogs; // temporary syntax for concat operator

base constraint works for type inference as well — of course noticing that Object is base of any other type is correct, but not very productive, thus for each constraint the lowest common ancestor (LCA) is computed. Still correct and probably exactly what you would like to see:

def twist<C,D,A>(b Bool,c C,d D) A
  where 
    A base C;
    A base D;

  return b ? c : d;
end

When you absorb this let Skila infer Animal type for you:

animal = twist(true,cat,dog);
Tagged , , ,