Monthly Archives: May 2016

It

Mundane tasks are for computers, not me — I was bored with all the typing class Iterator<T> ••• and then somewhere later ••• Iterator<T>. It is current class, why do I have to repeat myself? So I added alias to current (compile time) type — It.

Tagged ,

Starting over with errors

Current state

It didn’t take me long to realize I simply hate working with Option type as a replacement of exceptions — instead of simply writing the code I have to babysit each possible failing function and write conditions for each optional value (I am not the only one in pain). So I read again about Java, C#, Haskell, OCaml and Rust (did you notice Rust team introduced subtle flaw in design — in Haskell and OCaml the correct outcome of the function is the right outcome. In Rust it is on the left, so the right answer is the erroneous answer). I can sum this up as follows:

  • C# approach is a dead end because it leads to duplicating functions — dreadful Do and TryDo pattern,
  • Java annotated functions with exceptions that can be thrown and passed was a mistake, it only takes few minutes of writing the code to feel tired. And for unchecked exceptions it shares the same problem as C#,
  • Haskell looked promising until I saw the real code tackling the errors — so partially I would be in camp I want to escape from already (Skila), and partially in heavy function annotating camp which I dislike since my Java experience. Also I am sceptical how smooth this solution is when it comes to inheritance and when you read “Error handling” chapter (from Real World Haskell by Bryan O’Sullivan, John Goerzen, Don Stewart) you might get the impression that something is fishy, if one feature kills another feature and you need to jump extra hoops to use both.

I have wrong design (more in artistic sense) and three approaches which, well, I don’t like.

I could agree with the sentence “if you don’t have anything right to say, or anything that moves the art forward, then you’d better just be completely silent and neutral, as opposed to trying to lay out a framework” (from interview with Anders Hejlsberg) but not in this case — working on and implementing the language which is broken from start is no fun, and has no value at all. I must make a second, third — fourth if necessary — try to find out the solution. After all it has to be somewhere.

Envisioned design

Skila will have exceptions but they will be thrown usually by code injected by compiler. All errors in computations should be signalled by:

  • optional value — if it is likely that user will handle the error. It is counterpart of function that requires its outcome to be read. As an example consider String.indexOf — not finding character in string could lead to an error, but it is rather not an error by itself,
  • Error type — here the situation is reversed, it is more likely that the “client” would treat an error like a serious computation failure and pass it for handling to some upper function, it is counterpart of the function that allows its outcome to be ignored. Every unhandled error is automatically converted to exception. Error type will be subtype of any other type, so returning an Error instead of “real” value does not need to (and cannot?) be annotated in function signature. In other words every function can fail.

You can convert optional value to exception (this is already done), you can convert error to exception or to optional value. The beauty I foresee is present in several aspects:

  • it is transparent to inheritance,
  • it should not clash with any other feature,
  • single function will be enough,
  • the flow of the errors is layered,
  • no extra code necessary — you want to cope with errors, you do it, you don‘t want to — simply behave naturally (as with exceptions).

What is layered flow of errors? By default, the function you talk directly to could give you an error, but all third party functions involved in computation can result in exception (because every unhandled error becomes an exception). You could depict such propagation in default flow:

deep function ⟶ nearby function ⟶ your function
  (error)      |                  |
               |     (error)      |
               |   (exception)    |
               |                  |   (exception)

In the first line you see deep function returns an error. The error is passed to the nearby function (line 2), since it is incoming error and it is unhandled it becomes an exception (line 3), it is then “passed” to your function. In more real life perspective, when you call for example dict[key] (nearby function) for non existing key you will get error and you can handle the error right away. But if the failure is caused because there is a some logging (deep function) involved, and that logging failed, you will get an exception, not an error.

Again, this is the default flow — every function can handle the values it gets and convert them to whatever necessary.

Problems

Entire mechanism is very similar to passing null in C# — in Skila it will be done on purpose, as a bait. But there is difference — in C# you can pass null without triggering exception, in Skila even passing has to be checked. This is for easier finding the source of the error and also to consistently process all the functions including those which work for their side effects (all Void functions are prime examples). So performance can be a victim here.

Advertisement
Tagged , , , , , ,

Arbitrary quotations

While working on html code I realized (for the n-th time) that having just verbatim and non-verbatim strings is way too little. Ruby did it right, so I simply copied proven solution:

let s = %s<<p>quotation counts "parentheses"</p>>;

In the process I reverted the syntax for characters literals back to C#:

let c = 'b';

at the expense of adding backtick as meta switch. In Ada apostrophe serves dual role but I don’t see how to make it in Skila grammar:

••• > ' •••

would be ambiguous (accessing meta information about generic type or comparing characters).

Tagged , , , , , ,

No more lies about static fields

When you declare a static field in C# it is not a field per se — it is a tiny getter which initializes the field on first read. I don’t like this approach because it misinforms the user — user wants a direct access but instead she or he gets a wrapper function. Small lie but still a lie. Initializing all static fields eagerly, even unused, is also not a good idea. So I tried to combine those two in a meaningful way…

Static fields can be initialized only with basic values and the initialization is done eagerly (plain old Int static field with initial value 100 is fine). Every other static field is forbidden.

For other types (and complex initial values) properties come to the rescue — properties are initialized lazily with added guards. The message is clear and the safety is guaranteed, in case of circular initialization you will get nice crash (I am not joking, on various occasions I noticed .Net 4.5 keeps working with silently inserted nulls).

Those changes effectively killed static variables so I made temporary exception in sake of testing them — any Int expression is treated as basic value. Another temporary leftovers are static constructors — I am not sure what to do with them, they will be limited to linear initialization or completely removed.

Full solution will take more time — sure bet are lazy types. With lazy types the notion for user is straightforward thus they could be used for all static fields. The other solution could be allowing function and namespace properties.

Tagged , , , , ,

Two faces of extensions

Before we begin and I forget — if you place any extension at the same file the type is defined you have full access to all private members of it. Phew, now we can begin…

External extensions

The idea of external extensions are taken from C# — I simply love it. The extension keyword and grouping is taken from Swift.

Those are pure syntactic sugar, there is nothing you couldn’t write by yourself using non-extension part of the language.

Micro extensions

Let’s say you have collection of collection of some elements, the common task is to flatten the data. You cannot add such method to Sequence type, but external method just fits right in:

extension def flatten<E>(this ~~E) ~E
  ••• 
end

Three things to notice:

  1. the function is placed directly in (any) namespace,
  2. the modifier is extension,
  3. there is explicit this parameter.

Such extension can be used as a function or as a method:

_ = flatten([[1,2],[3,4]]);
_ = [[1,2],[3,4]].flatten();

Extension types

Here we group the methods and properties into extension type — I save better example for later so let’s use absurd one:

extension class Real
  def square Real get => this*this;
  def cube Real get => square*this;
end

We didn’t change the source of the type. We just added new properties on the side:

_ = 4.5.square;
_ = 1.7.cube;

Please note in case of extension types, we don’t add explicitly this parameter and we can only call methods as, well, methods.

Internal extensions

I didn’t have time (and a book) to check in detail how much Skila differs from Swift but it seems there was a different need that drove the idea of this extension, thus Skila extensions are more limited. Currently I am reading about Objective-C — and surprise, surprise — I noticed it supports extensions.

Unlike C++ template specializations you cannot provide customized version of a given method present in a template. You can only add new methods (or properties) which do not exist in a template being extended.

Consider two sequences of some elements. Would it be useful to test whether those sequences are equal?

if [a,b] == [c,d] then
  ••• 

It didn’t work, because Sequence couldn’t blindly assume its elements support equality test. With our new tool we can add appropriate method. Our first try:

extension interface Sequence<T>
  where T refines Equatable;

  def ==(cmp ~T) Bool 
    ••• 
  end
end

With above external extension we can test equality of two sequences in safe manner (this is not true in C# which provides unsafe Equals — pretty much the same as in dynamic languages).

But is it really all? No — despite we can perform the test, we didn’t change the Sequence type so we cannot compare a sequence of a sequences of Equatable elements, because the sequence of Equatable elements does not inherit Equatable itself:

%% does not work with above extension
if [[a,b],[i,j]] == [[c,d],[k,l]] then
  ••• 

We need internal extension (specialization):

extension interface Sequence<T> refines Equatable
  where T refines Equatable;

  refines def ==(cmp ~T) Bool 
    ••• 
  end
end

What does it mean? A general Sequence type does not inherit from Equatable interface (because it cannot), but once we drop something that is Equatable inside Sequence type, we get type specialization which does inherit from Equatable.

There is more — a type which inherits from Sequence (like !Array) also behaves in the same way, its general version is not Equatable, but if you have let’s say array of numbers (which are Equatable) entire array becomes Equatable — think of it as one specialization inherits from another specialization.

Specializations can be combined — imaginary example, if we wrote specialization for Sequence to inherit from X when elements inherit from X, and in same manner we did with type Y what happens if we have Sequence of elements which derive from X and Y at the same time? We will get Sequence type inheriting from X and Y.

The bill? You have to place internal extensions in the same file as type being extended. Also you cannot extend non-template type (it does not make sense in Skila perspective, because there would be no way to discriminate such extension).

Tagged ,