2011
08.03

Expressions

A Kronos expression is essentially a function and the expression that is the argument to that function. Functions that receive no argument finish this vicious circle. Here are some expressions:

Crt:cos(x)
Crt:sin(Crt:cos(5))
4 + 9 / 3
(15 20 3 + 4 Crt:pow(2 5))

Note that this by itself is not a valid Kronos program. There are a couple of plainly obvious functions, such as ‘cos’ and ‘sin’. The prefix ‘Crt:’ tells that they reside in a package called Crt, containing functions lifted from the C standard runtime.

Less obvious is the line with arithmetics. These are infix functions - all the parser really does is to convert them back to regular functions. This particular line is transformed like so:

4 + 9 / 3 ~ Add(4 Div(9 3))

The parser honors mathematical operator precedence, which can be overridden by the use of parenthesis.

Definitions

Programming in Kronos is 99% definitions. If you like, it can be 100%, but some syntactic sugar can come in handy. A definition is given by the equality:

something = something-else

After this line, it makes no difference whether you use ‘something’ or ‘something-else’. Ever. Well, within the enclosing {} anyway.

Q: What if I change something-else after copying the value to something?

Whoever said anything about copying?

This is an important distinction between functional and procedural languages. Symbols are not ‘variables’ and cannot be changed; much like in mathematics,

x = 3
x = 5

is paradoxical and rejected with extreme prejudice.

A ‘symbol’ effectively starts to exist after a definition. In Kronos programs, symbols must be defined before being used.

/* Right */
x = 5
y = x + 3

/* Wrong */
y = x + 3
x = 5

Please note that k2cli will not accept a definition. Your input to the interactive parser must be a single expression and not a definition. All definitions must be placed in .k files that you load at launch.

In part 3 we learn about defining and using functions.

2011
08.03

k2cli is the command line interface to Kronos, the Just in Time compiler for musical signal processing. In this technology preview, it is a 32-bit Windows executable. Extensive platform support is coming later.

A command line overview can be accessed by using the switch -h

PS C:\Users\Vesa\Kronos.Preview1> .\k2cli.exe -h
K2CLI 0.1
(c) 2011 Vesa Norilo

Usage: k2cli [options] [files...]
[files...] will be parsed and imported before evaluation of <expression>

Options:
  -h or --help                        :  display this help
  -p or --profile                     :  report timing information for compilation and execution
  -i or --infer                       :  perform type inferral on expression and report
  -q or --quiet                       :  suppress output apart from result
  -e <expr> or --expression <expr>    :  evaluate the expression <expr>
  -w <path> or --write <path>         :  write audio output to file <path>
  -l <count> or --loop <count>        :  repeat the execution <count> times
  --console <expr>                    :  print reactive output of <expr>
  --audio-out <expr>                  :  derive a real time audio stream from <expr>
  --setup                             :  enumerate available hardware and compilers
  --audio-dev <out> <in>              :  use device indices <out> and <in> for audio i/o
  --break                             :  insert breakpoint for debugging

The quickest way to talk to Kronos is to use the interactive mode. Launch the application without any switches and enter a mathematical expression like so:

PS C:\Users\Vesa\Kronos.Preview1> .\k2cli.exe
K2CLI 0.1
(c) 2011 Vesa Norilo

Type 'k2cli -h' for help. Enter an expression to evaluate or nothing to quit.

EXPR>1 + 1
1 + 1 => 2
EXPR>

Note the whitespace around all tokens. All infix functions, +, -, * and / have the same requirement. Without whitespace, infix characters are considered to be part of a symbol. For example

Make-Love-Not-War(60)

Is a valid function call, not a difference of four variables Make, Love, Not and War. I realize some people will hate me for this, but I prefer the code to look this way. You can always drop infix functions altogether and write

Add(Mul(3 5) 2)

To compute 3*5+2.

Importing files

You can import functions from a text file. By convention, all Kronos sources have the extension ‘.k’, but the compiler doesn’t really care. Let’s write a simple function and save it as ‘myfunc.k’

/* myfunc.k */
Take-Square(x)
{
	Take-Square = x * x
}

Now we can ask the command line interface to load up our file and use our function.

PS C:\Users\Vesa\Kronos.Preview1> .\k2cli.exe .\myfunc.k
K2CLI 0.1
(c) 2011 Vesa Norilo

EXPR>Take-Square(3)
Take-Square(3) => 9

Fantastic! In part 2, we will cover programming basics.

2011
08.03

Dear All & Sundry,

This blog post is a low profile launch for Kronos the compiler suite. Low because the system is not quite ready for wider publicity, but a launch anyway since enough interest has been generated for a centralized resource to be useful.

You can download the technology preview to try on Windows/x86.

Tutorials are provided here on the blog.

2011
07.26

Kronos, the compiler/language I am working on, belongs to the family of functional programming languages. This post is intended to be a simple primer on what to expect if you are coming from an imperative laguage.

No idea what functional or imperative means? In that case, if you have done programming, chances are it has been more imperative than functional.

The simplest analogy I know is that an imperative program is a bit like a recipe. There is a list of ingredients. There is a series of instructions, each changing the state of one or more ingredients (such as mashing potatoes). By following the recipe, the end state is hopefully well known and delicious.

Functional programs, on the other hand, are like mathematical, well, functions. There are no ingredients, no instructions. Just a description of how the end result depends on a set of defined parameters.

Recipes can certainly feel more intuitive than equations. This is a double-edged sword – while we can reason about equations and prove and disprove them, about recipes we just have gut feelings. Computers, and by extension, compilers, are notoriously bad at gut feelings. On the other hand, when instructed appropriately, they can use formal reasoning and proofs to optimize your programs. That is why a functional program can be much further optimized than an imperative program.

This is not to say that functional programs are automatically faster than imperative programs. On the contrary. Most of the best performing code written in the world is probably imperative. However, high performance code has traditionally meant carefully hand-tuned imperative code. Functional programs can be pretty darn fast, yet expressed much more easily.

But I digress. This was supposed to be a ‘gentle introduction’.

Ingredients and Instructions

Simply put, the ingredients from the recipe analogy are the variables in your imperative program. Together they determine its state space. Just think about the concept of variable for a while. Nomen est omen. The content of the variable varies, or changes, during the course of the program. The program consists of instructions that tell the computer how to mash the variables.

Functions and Flow graphs

On the other hand, a functional program has no variables. The program has no state space and the entire output depends solely on the input, just like a mathematical function. The program is essentially a flow graph, connecting inputs and outputs.

To illustrate the difference, here are two functions, both of them written in C++.

void Append_Imperative(std::string& first, std::string& second)
{
  first+=second;
}

string Append_Functional(const std::string first, const std::string second)
{
  return first + second;
}

In all their banality these functions represent the differing ideas of imperative and functional programming. While the first function changes or mutates its first parameter, the second one just creates a brand new string and returns it without mutation. I have used reference parameters in the imperative case to underline the fact that the called function might do anything to our program state. Likewise, the const keyword represents the stateless behavior of the functional case.

Isn’t the imperative version faster? Probably, in C++ at least. Then again, if C++ were a functional language, the compiler might well be able to produce identical code. Since it is not, it is devilishly hard to analyze the code thoroughly enough to safely engage such optimizations.

Why bother?

Think of functional programming as an agreement. You agree not to use variables because they make the compiler’s job a lot harder. In return, the compiler will be able to optimize much better, allowing you to not worry about implementation details such as passing data by value or by reference. You will also begin to see that the use of variables is one of the great sources of programming errors in a practical system. To keep up with the kitchen metaphors, functional is like a diet – it can feel incredibly tough at first, but chances are it makes you healthier.

Much like there are no miracle diets, functional programming will not by itself make you a good programmer. Further, it is important to choose a paradigm and even mix elements from functional, imperative, object-oriented, declarative, aspect-oriented and whatnot based upon the task in hand. Functional programming is exceptionally well suited to managing data flows, such as signal processing and mathematics. It may not be as useful in writing an operating system, a GUI framework or a word processor. I haven’t tried.

But, the diet. If you are to give up variables, what remains? Read on to part 2 to find out how we manage splendidly in the stateless, variable-less club.

2011
07.05

Object-oriented programming used to be the new rage. So much so that it became mainstream and taken for granted. In fact, I feel that it is fashionable to bash OOP among the hip nerds, and being a slave to fashion I’ll add my voice to theirs.

So, what is OOP all about? I will paraphrase someone here; it bugs me to no end that I can’t recall the source, maybe someone can let me know who it was, maybe even the exact quote.

Modularization of procedural programs makes code reusable by allowing new code to utilize old code.

Object-oriented design makes code reusable by allowing old code to utilize new code.

Fair enough – that’s not a bad thing to accomplish. I think the essential substance of OOP is that it formalizes the way we define protocols, so that code can be written to talk to a protocol instead of an implementation. Once the protocol is in place, implementations can be provided even after the fact. Inheritance and encapsulation are really only ways of facilitating this.

But how does OOP make that possible? Better to ask, why was it not possible before?

LISP programmers were never that big on OOP. In fact, when you think on it, the biggest proponents of OOP tend to be C people who have had a Java or C++ awakening with the odd Smalltalk guy here and there. The recovering C-programmer might be gushing about how his new object overlords can combine data and functions under a type name. To which the LISP programmer would reply; “What’s new about first class functions anyway? And what do you need that type name for?”

It makes a great deal of sense; low level languages tend to separate functions and data, as well as offer minimal metaprogramming capabilities. Their merit has been and still is the efficient machine code produced by the compilers, C as the prime example.

C++ addresses the metaprogramming deficit in C in its typical lumbering manner. There is the hulking template compiler, immensely powerful yet frightening and syntactically confused. It offers syntactic sugar on function pointers (some people call them classes and virtual functions) and ways to tell the compiler to stop you from doing stuff you don’t want you to do. C++ is an odd programming language, but still the most powerful C code generator widely used and as such immensely useful.

Objective C, the Smalltalk mantle-bearer, snorts derisively at the amount of “stuff” in C++. It comes with a different flavour of dispatch wrapping, implemented with a very small extension to C syntax and a run time library. It entirely fails to address metaprogramming. Indeed, some C++ people complain about how Obj-C programs can not be made type safe. That is somewhat misguided as Objective-C is quite bad as a statically typed language. Dynamically typed programs are comfortable to write in Obj-C, but somehow it seems to me to defeat the purpose of welding an object system onto C. Why not stick to Smalltalk?

Java is a reaction to C++, based on the entirely acute observation that C++ makes bad programmers out of beginners. As reactions often are, this one feels somewhat misguided. Designed on the assumption that to make C++ beautiful you need to chop off the ugly parts, it ends up being a mutilated C++ with a garbage collector and slower execution.

C# epitomizes the modern day Microsoft to me. Seemingly started as “Let’s make Java with a different name” the language has evolved into a decent object language. It is the prettiest daughter of the C family, but dismally “same old”; sugar on function pointers.

To come back to the LISP programmer’s suprise about someone’s delight in being able to combine funtions and data; objects and classes sure seem fancy. If the language you tack them on can’t do much.1

 

  1. Of course, the C programmer will be happy to sunbathe on the beach, having executed his/her program to wrap up the workday, while the LISP program is still busy figuring out what is the essence of data and where it might be found. []
2011
07.02

Indeed. If one could unequivocally answer that question, untold riches both cerebral and mundane would no doubt await. Much like many other complicated questions in human life, it may well not have a single well-defined answer.

However, it is something I must obviously care about since I chose to get into making compilers.

Ask any programmer, and he/she will likely come up with an intuitive answer quite quickly. The language is fast. It is easy to learn. It is object oriented. Or dynamic.

Performance and Correctness

Some of those qualities are about the kind of machine code the language ends up weaving. This was the be-all-end-all consideration in the old days, when machine code had to be quite frugal to make programs even remotely usable.

Nowadays, as system complexity has exploded, the chief usability concern may well be the correctness of the program. Therefore, some thought can and should be spared to the comfort of the programmer, frantically hammering at his/her keyboard. A program that runs fast is of no use if it doesn’t run correctly. Most ‘good’ features of a programming languages that don’t affect the quality of the produced machine code relate to this category of concerns.

Programmer comfort can naturally be increased by handling some programming tasks automatically. Register allocation has long since been delegated to the compiler, rightly so. As processors get stranger and stranger and compilers get better and better, a diminishing fraction of programmers can actually best the compiler in producing machine code.

What happened to register allocation seems to be happening to memory allocation at the moment. There is a familiar subtext – in a huge, concurrent system memory management has become complicated enough to warrant some really bright people to come up with a garbage collector – and it will eventually outperform the tailor-made solutions of most programmers, much like industrial shoes will outperform tailor-made shoes economically.

This is all technological progress. But there is another trend that is slightly more worrisome. Not to mention the fact that we are all rooting for tailors and handicraft.

Bad Languages for Bad Programmers

Modern language design seems to assume that programmers are bad. Sad to say, this may well be the correct assumption. However, keep at it long enough, and the prophecy is sure to fulfill itself.

So, in effect, languages are designed to prevent programmers from doing bad things.

I’m all for choosing not to do bad things. In fact, even when programming in C++, I’ve come to regard using mutable data as an optimization – something that should never be prematurely done. However, I will often choose to use mutable data anyway.

What I will not tolerate is the language preventing me from doing bad things.

In the spirit of optimism and programming hubris, let us declare that good programming languages are those that enable us to do things and bad (maybe evil?) programming languages are those that seek to lock us into a paradigm, in effect disabling us.

 

 

2011
07.02

Welcome!

This blog is where I will dump miscellaneous thoughts and ideas about using computers to process audio signals.

These are more or less related to my doctoral study project, Kronos. However, since I dabble in various fields, the topics may diverge rather wildly.

Shout out to blogcrowds for the WordPress theme.