Tag Archives: generics

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…

Advertisement
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 , , ,

Generic static fields — see you next year

I wanted to make a mental note at the end of 2015 — “feature freeze”. It won’t happen, I cannot make my mind what is the right choice.

The natural decision would be to copycat C# — each constructed type has its own copy of static fields which allows to use generic static fields. Resharper wiki warns about this approach — “in the vast majority of cases, having a static field in a generic type is a sign of an error” ¹. We can find simpler one in Java (more for historical reasons than technical, but still) — it does not support generic static fields and it shares static fields among all constructed types. Scala takes another angle for the same model — since static data can live only in companion objects and they cannot have type parameters the issue is solved by design. As for Swift it is an open question because as the error message says static fields are “not yet supported in generic types”.

C# has some peculiarities — till now I didn’t figure out why C# makes instances of static constructor (see Accelerated C# 2010 by Trey Nash, p.263). Skila would have to add its own because of static variables in generic methods. I feel really insecure here so I wonder whether C# model is worth the effort?

A memento from C++ world:

The original intention of C++ template support envisioned a use-directed automatic instantiation mechanism that required neither user intervention nor multiple instantiations of the same file. This has proved considerably more difficult to achieve than anyone at the time imagined (see [STROUP94]).

— Stanley B.Lippman, Inside the C++ Object Model

Sure, it is not the same issue, over 22 years passed, nevertheless I still have the chills. Until I have firm knowledge or burning desire for this feature I’d better follow Swift footsteps — not supported. Yet.


¹ I was curious what “vast majority” means so I contacted the author of that entry, Dmitri Nesteruk. As it turned out it doesn’t translate to any numbers, it was just a generalization based on the nature of C# model.

Tagged , , , , , , , , , ,

What is easy — generics with nullable types not for sure

For a week I have been removing features from Skila (so far just two) and postponing some nice things for later implementation — I still want Skila to look at least nicely. Ironically that easy subset is not that easy to grasp, currently I hit hard on nullable types. So far I could see three camps:

  • C# — implicit null-check with null values (there are no referential non-nullable types though),
  • Kotlin camp — explicit null-check with null values,
  • Swift camp — as Kotlin but with wrapper class “Option” instead of null.

Take my words with grain of salt because I don’t know Kotlin or Swift.

Using “null” is not the same as having “Option”, the second can be wrapped as many times as you like when in case of the former you cannot wrap “null” into “null” — it is a value, not a wrapper.

On the other hand all checks designed for nullable type can be applied to non-nullable type, this is not true with optional types. For example one can blindly execute “x != null” check in Kotlin, while the analogous code in Swift requires “x” to be really an “Option”.

Worse, both Kotlin and Swift approaches bring problems to Skila, for example — how do you initialize objects of generic type? If I choose Kotlin solution how does inheritance tree look like for nullable types? And even if I choose Kotlin approach indeed I know that “Option” type will have to be added as well, so maybe add it right from the beginning… and so on and on.

Just to increase my confusion I found out some inconsistency in Kotlin:

open class Foo {
  fun foo() : Int {
    return 0;
  }
}

fun test<T : Foo?>(x : T) : Int {
  return x.foo() // (*)
}

fun main(args : Array<String>) {
  println(test<Foo?>(null))
} 

Compiler should demand “x!!.foo()” in line marked with “(*)”, but the code above compiles without any errors. And it worries me for Skila — those two ideas (generics with nullable/optional types) are not easy and it is hard to foresee (at least for me) all the problems with writing generic classes in practice.

If you are curious you can play online with both languages — playground for Swift and for Kotlin.

Tagged , , , , , , ,

What’s cooking — type generics and method’s zoo

I’ve just finished generics with type parameters, no surprise since I already implemented symbol generics. So far Skila does not have constraints, but I prefer to cover more ground in order to have working language in hand. Collections (and passing them around) is my next step.

Since this will take some time, I might make a break and add static virtual methods. You can say — “what? How a static method can be virtual”. Well, in C++/C# it cannot, because it was implemented that way. All it takes is one look from different perspective — take a regular virtual method and limit its access to static features only. Here you have it — static virtual method.

It is not a feature I will die for, but I too often bump into a problem which could be nicely solved having static virtual method and its cousin — pinned method (new concept), static or not. Pinned method is just like virtual one but it is erased with each new class in hierarchy, meaning once you add it somewhere you have to redefine it in each descendant class. This is pretty similar to pinned constructors (which are already implemented in Skila).

With such arsenal you can finally decide what is better for you, static factories or per instance, you can handle some traits of the class (type) with static methods. Nothing revolutionary, just more freedom and flexibility.

And since there is already so much variety why not add yet another one, with even more limited access — actually with no access at all. Function/method which relies only on its parameters with no side effects, pure function. We’ll see…

Tagged , , , , , ,

Uncharted territory — symbol generics

I had a little problem with this feature — not only I’ve recently got a job and I have much less time for developing Skila, but I had no luck finding a source explaining how you usually implement generics in compiler. Nevertheless I started with the flavor which bugged me since C++, namely symbol generics.

Consider a pair for C++, taking regular approach you won’t make anything more than a structure with “first” and “second” fields. The code works of course, but the moment you pass such data you have to comment a lot of code, because “first” carries absolutely zero information. Is this an address? Or salary? Or weight?

In C++ I was not completely lost — I wrote “NamedTuple” macro and then I had regular fields as above plus reference fields with the names I passed. Thus I could pass named tuple to any template function which expected regular tuple (because I had “first”, “second”, and so on, fields). The downside was as usual with macros in C++ – they are harder to maintain.

I am not against introducing macros to Skila, but I’ll wait until they become necessity. And with symbol generics I can do more than I did in C++:

class Tuple<'ITEM1'>
  var _1 Int = 5;
  alias ITEM1 _1;
end

func main() Int
do
  var m = new Tuple<'Id'>();
  return m.Id;
end

First of all, you deal with compiler, not preprocessor. Second — you don’t use references, but aliases, they don’t take space at all, so there is no penalty in taken memory. And third — it works. Here, you can see I instantiate the “Tuple” class with symbol “Id” and from that point I don’t use meaningless “_1” but “Id”. No need for extra comment stating “_1 holds Id”.

One of my idées fixes… fixed!

Tagged , , , , ,

Warming up for generics — aliases

It won’t be that easy with main target, but warming up was no so bad — I added aliases in about an hour or so. Currently only fields in given class can be aliased, but it is enough for the purpose of having generics implemented.

class Foo
  var mine Int = 5;
  alias other mine;
  
  func main() @Int 
  do
    return other;
  end
end

In target code “other” completely vanishes — it takes no space, it is just what the name says, an alias.

Tagged ,

SELF — one factory to rule them all

Another promise fulfilled — having pinned constructors in hand I could add “SELF” which behaves like implicit generics. Or you can think of it as virtual static feature.

Probably the best way to present it is using factory pattern:

struct Foo
  // constructor
  def pinned init() Foo 
  do
    return self // "this" in C++ family
  end

  def static Create() SELF
  do
    return new SELF();
  end
end

struct Bar : Foo
  def pinned init() Bar
  do
    return self 
  end
end  

The “SELF” which interests us sits in “Create” method. When you call it as “Foo.Create” you will get instance of “Foo”, but when you call it as “Bar.Create” you will get “Bar” object. Please note that such call does not cause an error:

var obj Bar = Bar.Create();

The above snippet would be erroneous if “Create” was defined differently, namely:

  ...
  def static Create() Foo
  do
    return new SELF();
  end

forcing you to write:

var obj Foo = Bar.Create(); 
// Bar is created nevertheless

But since it was defined with result type as “SELF”, the actual result type changes from call to call and compiler knows about it.

To summarize — “self” is a polymorphic reference to an object, “SELF” is a polymorphic reference to a type. You can create new instances using “SELF” as long as the used constructor is marked as pinned.

Tagged , , , ,

Implicit generics and pinned constructors

It is preliminary step of enhanced generics we know from C# and I hope all things finally clicked in for implementing this feature. Consider such code:

public class ClassModifier : TreeNode
{
  public bool IsAbstract { get; private set; }
  public bool IsVirtual { get; private set; }
  public bool IsStatic { get; private set; }

  private ClassModifier(NodeCoords coords)
    : base(coords)
  {
  }

  public static ClassModifier Abstract(NodeCoords coords)
  {
    return new ClassModifier(coords) 
      { IsAbstract = true };
  }
  ...

and

public class MethodModifier : TreeNode
{
  public bool IsAbstract { get; private set; }
  public bool IsVirtual { get; private set; }
  public bool IsStatic { get; private set; }
  public bool IsNew { get; private set; }
  public bool IsOverride { get; private set; }

  private MethodModifier(NodeCoords coords)
    : base(coords)
  {
  }

  public static MethodModifier Abstract(NodeCoords coords)
  {
    return new MethodModifier(coords) 
      { IsAbstract = true };
  }
...

Factory methods as usual, nothing special except the code is highly redundant. In PHP it can be expressed nicely because it has static constructor with late binding — it works like static polymorphism. It can be shortened in C# too, but with ugly twists.

However Skila is meant to be expressive and compiled language.

So I came up with idea of pinned constructor for the beginning. Once added to any class such constructor has to be overridden in every descendant class (or automatically upgraded). Secondly special keyword to express static polymorphism is needed — “SELF” sounds just right. With those two enhancements the code becomes much shorter (it is not Skila yet):

public class ClassModifier : TreeNode
{
  public bool IsAbstract { get; protected set; }
  public bool IsVirtual { get; protected set; }
  public bool IsStatic { get; protected set; }

  protected pinned ClassModifier(NodeCoords coords)
    : base(coords)
  {
  }

  public static SELF Abstract(NodeCoords coords)
  {
    return new SELF(coords) 
      { IsAbstract = true };
  }
  ...

public class MethodModifier : ClassModifier
{
  public bool IsNew { get; private set; }
  public bool IsOverride { get; private set; }
  ...
}

Thanks to pinned constructor it is safe to write “new SELF(coords)” — every class has to support such call. And because “SELF” works as implicit generics when I call “MethodModifier.Abstract()” I will get “MethodModifier”, not “ClassModifier” despite the fact the method is static and it is defined in “ClassModifier” class.

Tagged , , , , , , ,