Monthly Archives: June 2016

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

Performance crisis

The last days were really tough for me because everything started to fall apart — compiler was way too slow to experiment with, programs written in Skila were too slow to run and allocated too much memory. To build DOM tree out of 1MB web page — single web page is taking nowadays over one million bytes? that’s insane — I needed over 0.5GB of memory.

Without solving those issues I would have to stop development of Skila, using other backend for speed and memory has no sense, since I am still experimenting with language features. So I had to blend in with smarter internal wiring.

First I dropped basic types from the backend — Char, Int and Real. They were just wrappers and because of their ubiquity they took a lot of extra space in total. Currently I use native PHP values and redirect them to appropriate static classes — when I know in advance I have for example Int type in hand I call static method for it. When I don’t know what I have I call small dispatcher which checks the types in runtime and makes appropriate call. Luckily PHP makes such task trivial. I saved around 60% memory with this move.

It was promising but not enough — I still had the bottleneck and its name was String (another commonly used type) which was implemented as Skila Array of Chars. When you split 210KB string into an PHP array of characters (since PHP does not have Char type it is an array of strings really) you will get 14MB array — in other words single character takes around 60 bytes. You can reduce the memory a little by converting an array to splFixedArray, but there is no enough gain here (not mentioning time required for all back and forth conversions). So I decided to simply wrap the native string with my type and rewrite all String methods. That move was big time winner — from initial 0.5GB the memory requirement dropped to 7MB and the speed is close to native PHP code, because now in fact it is almost native PHP code.

Currently I am going to make two micro optimizations — postponing parameter default initialization and adding inline functions. Both for sole purpose — speeding up assert execution.

With compiler I was not so successful — I use rich AST with pointers to parent’s node. It is really comfortable to work with such tree (and worse — I got used to it) but there is penalty in performance, you have to clone nodes whenever you would like to share some subtree. I don’t know how I will get rid of it completely, nevertheless the diet is already implemented with positive effect — compile time was reduced by 50%.

A lot of work ahead of me, but at least I managed to get out of trouble — I can continue my journey…

Tagged , ,

No point for being tidy

After some consideration I dropped checking formatting of the code. My reasoning was this — say you have 1000 errors just because you didn’t indent the code correctly. Will you fix it by hand or automatically? I guess you choose the latter, but in such case what is the point of checking the formatting in the first place? To duplicate testing correctness of your IDE?

There is a second reason — you come up with some great idea, you start coding, you compile and now not only you have to fix some technical errors but also you need to clean up the indentations. It does not sound very helpful, when you have great idea, you should focus on that idea, nothing else matters. So checking formatting is not solving the problem, and if you are not the solution of the problem, you are part of the problem.

It does not mean I am supporting messed up code, I am saying it is job of IDE which should (probably) reformat the code on final save, so when you open your code next time, you will see all the cleaning is already done.

Tagged

Introducing nearby exceptions

While working on this post, I discovered that Swift uses already very similar approach of handling the errors.

The implementation is cheapest I could think of, I am aware of the problems with backend, but enough with excuses — it works, so I will be able to play with it and test if the model is useful or not.

To keep things short let’s start with C# approach which is used in Skila as well:

Errors as exceptions

You access an array with index out of its boundary. You divide by zero. In cases like those you throw an exception to indicate the error in computation.

Errors as values

Your common sense, practice, intuition, tells you that for some computations throwing an exception would be too harsh, so instead you return magic value instead to indicate “not a value” value. Probably the best example is String.indexOf — for such purpose Skila has type Option<T> and special value null (it is not null you know from C# — it is like Scala none or null in Swift or Kotlin).

Ok, that’s it.

Wait, we missed the magic ingredient which makes exceptions fun to work with! Consider accessing the dictionary — to provide similar API as with arrays, when the key is not found the exception is thrown, it happens in C# and Skila as well. But it would be useful to be able to try to fetch the value — in C# you have to add another function to the class (TryGetValue in this case). In Skila you convert one mode (error as exception) to the other one (error as value):

let dict = {String:Int}();
let val ?Int = dict["hi"] $;  

Let’s focus on the second line — when we access the dictionary (it is empty) the exception is thrown from the indexer getter. That exception in Skila is special — it is nearby exception (because it is thrown in nearby, or next if you like, function we just called). Then there is dollar — $ — character which works as an exception trap. Only nearby exceptions can be trapped (the others go through) — and if this happens, they are converted to null value.

You don’t have to write duplicated code as in C#, you simply convert errors as you like — go from null to exception, or from exception to null. Both conversions are expressions and they are pretty accurate:

def thrower() String => throw Exception();

let dict = {String:Int}();
let val ?Int = dict[thrower()] $;  

Can you tell what will happen? The program will crash — that is because we trapped exception coming from dictionary indexer, not a thrower.

Why not trap all exceptions? Because I would like to distinguish between three outcomes in total — we looked and we found the key, we looked and we didn’t find the key (i.e. we are sure the key is not there), we crashed while looking (because, say, our comparison function is buggy), so we cannot tell if the key exists or not.

Maybe it is an overkill but after a day of playing with exception trap I felt I miss one more feature — consider reading an ini file and then adding key and value pair to properties dictionary:

properties!add(key,value);

When the key already exists we will get exception with a message and stack trace. However we won’t get plenty of information about the context of the problem, like the filename or line number. Sure, we can debug, but we might add those information right away:

properties!add(key,value) && "File ${filename}, line ${line}.";

The mechanism is old as exceptions — chaining. You can chain an exception or a string (Skila will create basic Exception for you). Since the new exception is created it is equivalent of explicit throw, i.e. for the outer function such chained exception will be a nearby exception.

And to just describe all the tools required for the job — there has to be a way to close the distance of the exception, otherwise assert and fail would be useless:

def [key K] V
set:
  •••
  fail("Key not found.");
end

Your code calls dictionary indexer, dictionary indexer calls fail, fail calls assert, assert throws an exception. So for sure such far placed throw will not be considered as “nearby” from your code perspective. Unless we tamper with the exception distance counter:

def assert(•••) Void
  •••
  throw Exception(•••) &&>>;
end

def fail(•••) Void
  assert(•••) &&>>;
end

This cryptic code &&>> tells compiler to pass an exception further. Effectively when you write assert or fail the exception which is thrown thinks it originated from where those functions were called.

Tagged , ,