Category Archives: Skila features

Object (post) initialization

Skila-1 had auto constructor as its object initialization version, but few days ago I was writing some ordinary Config type in C# and when I added builder pattern I realized how little C# is missing to get proper object initialization.

Thus I scratched the idea of reimplementing Skila-1 approach and I added object (post) initialization — it is much simpler to implement and it feels completely like C# (whenever I can I prefer to stick to already popular language). Using C# syntax it is just:

public class Point
{
    public init double X { get; }
    public init double Y { get; }
}

var p = new Point() { X = 5, Y = 7 };
p.X = 3; // error

Yes, all it takes is little modifier added to field/property indicating that despite it is readonly it is allowed to be assigned (initialized) during post initialization. The type itself is immutable, there is no additional mechanism added, everybody is happy.

The older approach has its advantages (single initialization for starter) but since it can be added in any time I will stick with this lightweight approach for now.

Advertisement
Tagged , ,

Complete null safety

Embarrassingly, it took me almost seven years to notice the full meaning of Option/Nullable type combined with mechanism like pattern matching.

The first layer is pretty obvious — consider such language as C# and its struct type. It is totally safe to use because it does not allow null. The separate world is nullable struct, here you can use null but your code is safer anyway because there is distinction between pure struct instance and nullable one. Until you write:

nullable_printer.Value.Print();

You pass null and boom, you have exception in your hands. The problem in this case is you can access the internals of nullable type.

Skila does not have pattern matching, but similarly it treats Option as black box — you cannot dereference it, you cannot access any of its member, the one thing you can do is decompose it. Conditionally:

if let printer ?= nullable_printer then
  printer.Print();
end

The optional declaration is successful only when we don’t have null. This way working with null is completely safe because compiler guarantees the logic flow is solid and you don‘t see any NullException. One thing less to worry, so I call it a progress.

Tagged , , , ,

Extending generic types with traits

I am pretty happy I managed to add the feature I was not able to while working on previous Skila — traits. Initially I thought about them as conditional methods:

class Greeter<T>
{
  void hello(T &t) where T : ISay
  {
    t.say();
  }
}

But when reading “Programming Rust” I noticed that syntax for Rust type implementations and this gave me the idea of decoupling those “conditional methods” from the main type:

class Greeter<T>
{
}

trait Greeter<T>
  where T : ISay
{
  void hello(T &t) 
  {
    t.say();
  }
}

It looks very much like extension method however you can spice it with inheritance:

class Greeter<T>
{
}

trait Greeter<T> : ISay
  where T : ISay
{
  void hello(T &t) 
  {
    t.say();
  }
  // traits support polymorphism
  override void say()
  {
    println("just saying");
  }
}

Depending which type you pass when constructing Greeter it will inherit from ISay or not.

At this point there is a little zoo in Skila with similar features — protocols and interfaces, traits and extensions (upcoming) — and I hope some of them will be merged. Simplicity matters.

Tagged , ,

Associated reference

I’ve just made up the term “associated reference”. While many features in Skila are simply borrowed from other languages I have never encountered this one before. And it looks bad — it is complex, fragile and I would gladly get rid of it, but I can’t, and here is why…

Consider such case as passing few arguments to C# variadic function. Variadic parameters in C# are arrays, arrays are allocated on heap, and this is inefficient. Skila has limited support for C++ like references, so in Skila we create array on stack and pass a reference to it to the function. It is efficient. Step one sounds good?

Step two — we are inside the function and we would like to iterate over the variadic parameter. We need an iterator for it, and in turn iterator needs a collection to iterate over. Passing the collection as a value would mean copying collection, passing it as a pointer is desirable, but in a case like the current one we don’t have a pointer, just a reference. So the reference is the only option. The logic looks sound.

Step three — we need to store that reference inside iterator instance. And this makes the iterator a special kind of type — an associated reference.

Once introduced, all hell with validation breaks loose. For example associated reference type can have only single field with reference, it can have only one constructor with single parameter, which is of reference type. The associated reference type can be passed only by reference and its lifetime has to be limited to the life of the “seed” instance.

Fragile, complex, first feature which does not seem right.

And yet the only alternative I see is redesigning the concept of a stack by allowing to partially unwind it (to preserve referenced instances). At first glance it looks even worse than associated reference so I stick with it for a while.

Tagged , , ,

Memory chunks and inheriting enums

Type Chunk type represents a chunk of memory — either on stack or on heap. The latter use is pretty obvious, such types as Array or String will use it. The placement on stack is crucial for making variadic functions efficient — take for example C#, it supports variadic functions, but since arrays are allocated on heap, the comfort of using variadic functions comes at such price that it is not uncommon to see several overloads with unrolled parameters just to avoid variadic parameters.

Having Chunk allows efficient implementation of variadic functions — there is no memory allocation, just extra internal “parameter” (number of elements).

Unrelated to memory, another new feature is ability to inherit enums. From time to time I was annoyed I had to repeat enums in C# just to add few new entries (and remember to keep the same common values), so in Skila you simply derive one enum type from another and share common values. I am not sure if the inheritance is the correct mechanism here, because substitution rules are reversed for enums (you can pass base value when the derived type is required), but since it works I leave it as it is.

Ahead of me is reimplementing old rules of method-property derivation.

Tagged , , , ,

Recursive and ancestor calls — again

I‘ve just brought self and super calls back — more about these features in my previous post about them.

I am just not happy how super is implemented currently — I was forced to add initial stage of scanning all the signatures of types and functions in order to compute function derivation tables. So when I hit the body of the function — during the second stage of processing — I am able to bind super properly.

Tagged , ,

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).

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

Logical operators for null

Choosing the way of handling null with logical operators is basically a choice between three valued logic or typical “pragmatic” treating not-true values as false. I like the former approach but I chose the latter — it is faster to implement and run, probably it will be easier to grasp and most importantly I don’t see benefits of three valued logic. Every if branch is executed or not, there is no a third way for it, so if I ultimately ask “is this true” I can ask the same question right away when I see null.

Well, if I missed something here in worst case I will change the null logic.

Tagged , , ,

Inline functions

I jumped too prematurely to implementing inline functions as I would do it in a language compiled to machine code — namely just put the function instead of the call and that’s it. The problem with backends such as PHP or Javascript is you get the explosion of code which has to be processed by the backend engine, so instead of savings there would be a loss on duplicated code.

To make inline functions useful and predictable I inline them always (it is not just a hint as in C++) and such functions have draconian requirements — just single expression, parameters can be used once at most, no static data used and only standalone functions can be inlined. After reshaping assert to meet those criteria it looks as following:

inline def assert(cond ?Bool,msg String = "", 
                  details: String ... = []) Void 
  %% this code will be put instead of the call
  => assertPassed(cond) ? void : fail(msg,*details);

I did some unscientific tests and I was happy to observe there was no test running slower, in some cases I saved 25% of running time. No complains from me.

Tagged , ,