With the many new language features since the last (0.1.4) release, this tutorial is starting to get quite out of date. It's worth looking at as a starting point for understanding Kew, but isn't really a reliable reference any more. I'm going to update this shortly after the 0.2.0 release.
Kew has very little predefined syntax - almost everything is an object. Most of the code in the examples below actually is just using features of the standard library components that come with Kew.
For example, Kew doesn't have special built-in control flow operators like if-then-else. These things are done using objects called blocks.
Kew makes extensive use of blocks. These are similar to blocks in Smalltalk and lambda-expressions in Scheme and are probably the most important concept in the language. They have almost the same syntax as in Smalltalk.
Users of C-like or Pascal-like languages will find Kew blocks a lot more powerful than the blocks in those languages. This is because they are proper objects in their own right. You can pass a Kew block as an argument and have it executed somewhere else in your program. You can also put a block into a variable and execute it later, or as many times as you want.
. appears between statements - not after them. So the last statement in a block doesn't need to end with .
Blocks begin with [. This is followed by a list of arguments the block takes, each argument with a : before it. Arguments are divided by spaces. Next come some statements, separated by .. Blocks end with ].
[ :Argument1 :Argument2 :Argument3 Statement1. Statement2. Statement3 ]
A block returns no values unless you specify otherwise. The only place you can do that is the last statement in the block, using the return operator ^. For example, this block returns the object 2:
[ 1. ^2 ]
Blocks are allowed to return multiple values. Just write a ^ before each value to return:
[ ^1 ^2 ^3 ]
Later, it will be handy to know that the program component itself is a block - even though it has no explicit [ and ] surrounding it - and has its own closure. The only real difference is that it is dynamically loaded.
Blocks do something very powerful in Kew (and in some other languages like Scheme and Smalltalk) that sets them apart from languages like C and Pascal: they form closures. Without closures, a block cannot remember the environment it was created in. In C and Pascal it doesn't matter, because a block cannot be passed around as an object, so it doesn't matter whether it remembers its environment or not.
In Kew, where a block is an object that can be passed to another block, stored in a variable for later use, etc., it is important that it can remember its environment. Let's see a simple example:
'this object gets remembered in the blocks closure' :MyClosureValue. [ ^MyClosureValue ] :MyBlock.Here, we create a block object which just returns MyClosureValue. It gets that value from the program preceding it by looking up its name. That's no big deal, and languages like C can do this easily.
It gets more difficult when you pass MyBlock somewhere else and run it. Let's say we passed it into another block that runs it and prints the result:
'it would be easy to get confused and read this value' :MyClosureValue. [:ABlock Container log message (ABlock call) ] call MyBlockYou get the first MyClosureValue because the closure captured that string object as the block was created. You don't get the second one, even though it has the same name.
This is important, because it means that blocks do not get confused about the values they know: they remember the environment at the time they are created, not at the time they are used, which makes life easier for the programmer.
Closures turn out to be a very powerful concept, even though you can't "see" them. They can be used to create instance variables for objects and many other kinds of structures, by packing up an environment and preserving it, encapsulated in a block.
Blocks allow all kinds of powerful control flow constructs to be written. Here are some examples. By the way, DoSomething, DoSomethingElse and Condition (which can be either true or false) are just made up for these examples.
There is a construct for if-then. It looks a bit strange because the condition itself (strictly, the boolean value that it creates) that receives the message then and decides whether to run the block or not.
Condition then [ DoSomething ]
If-then-else needs two blocks: the first to run if the condition is true and the second to run if it is false.
Condition thenElse [ DoSomething ] [ DoSomethingElse ]
Booleans also understand else and elseThen. You can probably guess what they do.
While allows for a block to be repeated. The block is executed, and if it returns True it is executed again (and again, until it returns False).
[ Container log message 'repeating... '. ^True ] while
If you just want to run a block once, you can send it call. This is very useful if you have been passed a block object from elsewhere in the program. call can also be passed arguments, which are needed when the block itself requires some arguments.
[ Container log message 'running' ] :MyBlock. MyBlock call. [:Argument1 :Argument2 Container log message 'running' ] :MyBlockWithArguments. MyBlockWithArguments call 10 20
Integers, strings, booleans, floating-point numbers and a "nil" (or empty) object are all built into Kew and all understand a wide range of simple messages. Some examples of built-in objects:
'a string'. 100. -20. -1.03. 1.0e-17. True. False. Nil
Any object can be given a name that can be used to refer to it later in the program. The colon operator does this, as in this example.
10 + 20 :MyNamedObject.Any time after this is executed in the program you can refer to the result of 10 + 20 by placing MyNamedObject there instead.
You can use a name again to refer to a different object. This overrides the old meaning of the name. Note that this is not quite the same as assigning a new value to a variable. Names in Kew are constants. You can't assign new values to them. For that, you need a variable....
Kew provides a kind of object that allows you to store other objects in it and retrieve them whenever you like: a variable.
Note that this is different from names: a name cannot be changed once it is defined and is not an object - just a name for an object. A variable is an object that can hold another object. The object it holds can be changed at any time using an "assignment" message.
A variable does not need to be given a name and can be stored in a collection. This can be useful for more advanced programming.
We can create a new variable object and assign it a name like this:
Variable new :MyVariable.
A variable created like this always starts off holding Nil. To store another object into it, we can send it the message :=
MyVariable := 10
We can also read its value by sending it $
MyVariable $
Note that if you don't send a variable the message $, you are referring to the variable itself (which is also an object). In this example, DoSomething's using message is sent three times with three different arguments: 10, 20 and the variable object itself.
Variable new :MyVariable. MyVariable := 10. DoSomething using (MyVariable $). MyVariable := 20. DoSomething using (MyVariable $). DoSomething using MyVariable
If you want to assign an initial value when you create the variable, just pass the value to new:
Variable new 'initial value' :MyVariable.
As well as having built-in value objects for integers, strings, floating-point numbers, booleans and nil, Kew also has built-in collection objects. These are somewhat similar to the collection classes in Smalltalk and Java, and container classes in C++'s STL. Each collection can contain many objects and allow them to be accessed in some way, depending on what type of collection it is.
Currently, there are four types in Kew:
EmptyList & 'first item' & 2 & True
EmptyMap & 'key1' 'first item' & 'key2' 'second item'
EmptySet & 'key1' & 'key2' & 3
If this syntax looks a little weird that's because it isn't syntax: these collections are being created by sending messages to initially empty collections. This is similar to the way lists are built up in Scheme (using nil and cons).
Unlike most other languages, Kew collections cannot be changed once they have been created. Trying to add an object to a collection returns a new collection, just like adding 1 + 2 doesn't change 1 into 3!
This means you do not have to copy collections before passing them to another object (in case it tries to change the collection) or worry about changing a collection while you are iterating over it. This removes an annoying source of programming errors.
Finally, of course collections can hold any object, including ones that have been given names, blocks, objects and other collections:
10 + 20 :PredefinedName. EmptyList & PredefinedName & [:FirstNumber :SecondNumber ^FirstNumber + SecondNumber] & (EmptySet & 10 & PredefinedName)
You can create your own objects: { starts the creation of an object and } finishes it. So to create an object that does not understand any messages:
{ } :MyObject.
Kew objects are self-constructing - they are created as soon as the program reaches them. No other steps are required.
You can add behaviour to an object by listing all the message names (called selectors) you would like the object to respond to and the blocks that should be executed when the message is received.
{ doSomething [ Container log message 'I am doing something' ] doSomethingWithArguments [:AnArgument Container log message 'I got an argument'. Container log message AnArgument ] } :MyObject. MyObject doSomething. MyObject doSomethingWithArguments 'hello world'
Kew does not support inheritance, mainly because everything you can do with inheritance can be done better using compositions of smaller, simpler objects, but also because inheritance is not always compatible with the embedded worlds in which Kew is designed to be used.
Kew dispatches messages based not only on their names but also on the number of arguments and the number of results they return. We have already seen an example of that: variables understand new with no arguments, and new with one argument. Both methods return one result.
In Kew, these messages are sometimes referred to as new:0:1 and new:1:1. The first number is the number of arguments (the argument cardinality) and the second the number of results (the result cardinality). The full group of the message name and the two cardinalities is called the selector.
Your Kew objects can define any number of distinct selectors. So, for two methods to coexist in a single object their selector names must be different, and/or their argument cardinalities, and/or their result cardinalities.
There is no direct way to create an instance variable in Kew, because only method blocks are allowed to appear inside an object definition (in fact, that's all an object is: a collection mapping selectors to blocks).
Remember that all blocks form their own closures, so they can create and hold onto their own objects (e.g. a variable object) while they're executing. Of course, you want your instance variables to be shared among your method blocks, so to create an instance variable, you create a closure (inside another block) which is shared among all the method blocks, and put the variable there. That will need to be outside the { and } which create the object, since the method blocks can't share what hasn't been created yet.
Here's an example of constructing an object inside a closure defined by a block. You can think of this as being a bit like a constructor if you know C++ or Java. This is a block that, when run, creates an instance variable, then creates and returns an object. The scope of the InstanceVariable name is the outermost block, although the object it refers to can live forever.
[ Variable new :InstanceVariable. ^{ readInstanceVariable [ ^InstanceVariable $ ] writeInstanceVariable [:NewValue InstanceVariable := NewValue ] } ] :MakeAStatefulObject.To make a new object: MakeAStatefulObject call :MyNewObject.
To make a constructor that can take some arguments and set up the initial state of the object, you just need to add arguments to the block:
[:InitialValueOfInstanceVariable Variable new :InstanceVariable. InstanceVariable := InitialValueOfInstanceVariable. ^{ readInstanceVariable [ ^InstanceVariable $ ] writeInstanceVariable [:NewValue InstanceVariable := NewValue ] } ] :MakeAStatefulObject.Then use it with MakeAStatefulObject call 'my initial value' :MyNewObject. Even more simply, arguments also form part of the environment that a closure captures. Here is a simple value rememberer:
[:RememberedValue ^{ read [ ^RememberedValue ] } ] :Rememberer. Rememberer call 'remember this'This is a block that, when run, creates an object. That object contains a method selector read:0:1 which maps to a block. That block has to return RememberedValue, which is part of the environment (it's an argument of the outer block).
Later in the program when we send the read message, RememberedValue will be forgotten, so when the object which contains the read block is created, a closure is formed which captures RememberedValue and keeps it for as long as the object itself lives.
Remember that the program component itself sits inside a closure, so any names defined outside all explicit scopes have component scope.
Variable new := 'I can be shared by everything' :ComponentVariable. [ComponentVariable := (ComponentVariable $ , '!')] :AddExclaimation. { addExclaimation [ AddExclaimation call ] read [ ^ComponentVariable $ ] } :ComponentVariableAccessor.
Again, this works because of closures.
Exception handling is due for re-design in the next release of Kew.
Like most modern languages, Kew supports handling exceptions. Any object can be raised as the "problem" object. This will be passed through to the exception handler.
ProblemObjectException raise MyProblemObject
And of course you can also handle raised exceptions.
[ ... ] handle ProblemObjectException [:ProblemObject ... ]
You don't have to handle any particular exception object, though. Instead you can pass it on to the next exception handler by raising the same exception again in the handler.
One very important difference from most languages (for example, Java) is that Kew exceptions don't change the current position in the program. Throwing an exception will not enable you do avoid something that will be run later. For that, you need an escape.
You can define exceptions of your own. The Exception object creates exceptions when you send it the new message. For every exception you create you need to provide a default handler that says what to do when no other exception handler has been provided by the program. For example:
Exception new [:ProblemObject Container log error 'Unhandled exception'. Container shutdown. ] :MyException.
Escapes provide a way to perform jumps to later parts of the program, missing out some of the intermediate steps. This is often part of the function of exceptions in some languages (e.g. Java, C++). They have some distant similarities to C's longjmp feature, as well.
Escapes are objects that represent the "rest of the program". That probably sounds a bit weird unless you are familiar with continuations in the Scheme programming language. You can capture an escape at any point in the program's execution and use it like this:
[:Escape DoSomething. Escape result 10. DoSomethingElse ] escape :BlockResult. CarryOnRunning
The escape message causes the block to create a new escape object and start running using it as an argument. That argument ends up in Escape, and this represents what will happen in the program after this block finishes (i.e. CarryOnRunning).
After DoSomething, the escape is used by sending it do. This takes one argument: the "result" that is supposed to be returned to the rest of the program. This becomes the result of the block, and ends up in BlockResult. Slightly mind-boggling at first, but very powerful once you get used to it.
Smalltalk has a slightly weaker form of escape; the ^ return operator can be used to exit a method early. Scheme has full escape support, except that they are called continuations. In fact, the escape message is pretty much the same as Scheme's call/cc.
Ensured blocks are not yet implemented in Kew because of complex interactions with the security model I have planned.
Sometimes, when using an escape, actions that should always be taken get missed. A good example is when you have opened a stream to a file, then take an escape, the file stream is not automatically closed. To ensure that this action takes place, you can use an ensured block.
[:Escape [ MyFile open. Escape result Nil. DoSomething ] ensure [MyFile close] ] escape
When the escape is used in this example, DoSomething is never run. The ensured block [MyFile close] is always performed, even if the escape would normally ignore it, because it is protected by the ensure message.
Probably one of the last syntax-based features to be introduced to Kew is comments. In most programming languages, comments are just a form of space. They can appear almost anywhere and have no special meaning. One problem with this is that it can encourage people to write good comments to explain bad programming!
Comments in Kew can only appear in certain places, and they always have a special meaning. A comment starts and ends with a ". The first two places a comment can be used are at the start of a block or object definition. Placed here, the comment explains what the block or object does.
["adds one to its argument" :Argument ^Argument + 1 ]. {"stores a value" read [ ^Value $ ] write [:NewValue Value := NewValue ] }
For the object, its methods are blocks, so they can have comments too.
{"stores a value" read ["returns the current value" ^Value $ ] write ["writes in a new value" :NewValue Value := NewValue ] }
Next, any argument to a block can have a comment placed after it to explain what it is for or what kind of object it should be. A good way to avoid the need for argument comments is to give the arguments names that make it obvious what they are for. The following examples don't do that, just to show how a comment would be used, but could use better arguments names instead of comments.
["adds one to its argument" :Argument "a number" ^Argument + 1 ]. ["adds two arguments together" :ArgumentOne "first number" :ArgumentTwo "second number" ^Argument + 1 ].
The container object has a special place in Kew: it is the only point of contact with the outside world. Kew is designed to have a unified interface with all kinds of different containers. This will make it easy to write anything from standalone programs to library components to servlets and applets in a uniform way. Currently though, only a standalone container exists.
The container allows you to load additional library components to extend the functionality of your programs. Any number of these components can exist, although you may not have all of them installed. You can also write your own.
Whether or not a library component can be loaded depends on the container being able to find the one you ask for, and allowing you to access it. In some environments, you may not be able to load components that are actually installed because the activator will not allow it. An environment like this is usually called a sandbox and is used for running Kew programs that are not fully trustworthy in a safer way. Loading a component into the same container as the current component is accomplished like this:
Container applicationComponent "MyComponent" Container :MyComponent.
I'm still working on the relationship between a component and its container, but I have a simple working sandbox container. This is likely to change, so look at the source code for the latest examples. However, at the moment, all you have to do is make your own object to pass into the load method instead of Container. For example:
{ message [:Message Container log message 'SANDBOXED COMPONENT SAYS:'. Container log message Message ] } :SandboxLog. { log [ ^SandboxLog ] } :SandboxContainer. Container applicationComponent 'LoadedComponent' SandboxContainer :SandboxedComponent.All we are doing here is creating (a small part of) a container object and loading a component into it. This example will break down if the loaded component wants to do anything with its container other than log some messages. Of course, in a sandbox that might be exactly what you want.
Better late than never: the "Hello world" of Kew. It should look pretty familiar.
:Container Container output write 'hello world'. Container shutdown.
The first unusual feature is that the container object is passed in to your component as an argument. In future, it is likely that you will be able to pass in other arguments as well (components will become more like ordinary blocks).
The second unusual feature is that it finishes by instructing the container to shut down. If you leave that out, the container will continue to exist (although it takes no processing time). This is because the container services an event loop and you must ask that loop to stop.
The standalone container (kew) is event-driven and provides a couple of methods for installing callback blocks. The first asks for a block to be called on multiples of a time interval. The block must return True if it wants to be called again.
Container onInterval 1000 [Container output write 'tick' ^True]. Container onInterval 771 [Container output write 'tock' ^True].
The second is to install a callback for whenever the container has nothing else to do. Again, the block must return True if it wants to be called again.
Container onIdle [Container output write 'container is bored'. ^True]
Notice that here we are not shutting the container down. It will run forever servicing the event handlers we have installed.
Copyright 2004 Duncan Pierce, hosted by