New Book Review: Mule in Action
2015-03-09The second book review of the year: Mule in Action.
Link to the review hereThe second book review of the year: Mule in Action.
Link to the review here
I've had the opportunity to attend JDays 2015. Both the regular conference but also one of the workshops that was held in conjunction with the conference. To help me remember what I've learned and what cool new suff I came in contact with at the conference, I've decided to write a short blog entry about the talks I attended each day. Let's start with day 0...
As a prequel to the regular JDays conference I attended a one day workshop on Docker. It was held by Fredric Wendt from Growing Agility and Marcus Lönnberg from SpeedLedger. The aim of the workshop was to learn the basics of what Docker is, how it can be used to to deploy various servers/services and to practice doing just that.
The teacher had prepared a Vagrant box that we, the pupils, were required to download the day before the workshop started. On the Vagrant box, Docker and some other useful software were installed. This was a really good thing as it enabled us to start working on Docker-related things directly from the start of the workshop without wasting unecessary time on downloading and installing stuff. Another upside was that the common Vagrant box made the environments of all the attendantees equal, which made debugging of the exercises easier (equal environments for many is by the way on of the main benefits of Docker).
The actual workshop as split into lecturing sessions and practical exercises. Each (lecturing session, exercise) pair started with the teachers giving a short introduction to a specific Docker-related topic for about five to ten minutes. Then the attendees got the chance to try out the things descripted in a practical exercise, which most of the time took about twenty to thirty minutes. The teachers were available for helping out with problems or questions all of the time.
This way of working worked out very well I think. Me and my fellow workshoppers were on approximately the same level and held more or less the same pace when working with the exercises, which minimized the waiting times (which I, as the impatient person I am, appreciates ;)
The following topics were covered:
I think that that the pick of topics and the order they were presented was an obvious and good one. I did not feel as I missed something from the topic list.
My overall impression of the workshop is that it was really good. The course materials was fairly good, altough it contained some bugs and/or unclear stuff. The range of topics covered was excellent and I feel that I after the workshop have a quite good overall knowledge of what Docker is and how it can be used to create various execution environments.
I'm planning to play around a bit with Docker as I think it is a really nice and interesting piece of technology. One idea I have is to create one Docker container containing a MongoDB server and connect it to another Docker container having this blog running in either the Django development server (as a start) or Nginx (later on). This was I can play around with different versions of MongoDB and different webservers without having to be afraid of breaking something. This sounds really interesting and I hope I get around to doing it.
The following is a summary of what I remember from the sessions I went to the second day of JDays 2015.
Speaker: Tomas Nilsson, Oracle
Tomas described the release plan of Java 7, what it was it meant to contain and when it should been released "in six months" (said from 2005 and forward). It was meant to contain: Lambdas, Jigsaw, <> boiler plate code reduction, Strings in switches, try-with-resources and much more.
It was released in 2011. What did it contain then? It contained <> boiler plate code reduction, Strings in switches, try-with-resources.
What should Java 8 contain then? The stuff left out of Java 7. What did it contain? Lambdas, new Date framework.
So what will Java 9 contain...? The stuff left out of Java 8. Particularly Jigsaw.
Jigsaw is modularity for Java. Why? Because Java is a monolith which have a quite large memory footprint. With better modularity this footprint can be reduced. Modularity also promotes better security (because one can reduce the number of loaded classes and internal classes can be hidden).
Java 9 will also contain value classes: classes whose objects represent some kind of value. Such objects will not contain any ID (=> they cannot be compared by identity). value class Foo {...}
Summary: Very interesting! Contained much info that can be useful later on when Java 9 is released.
Rating: 4 (good English, good content)
Speaker: Masoud Kalali
The talk gave a general overview of asynchronous technologies in the JavaEE sphere. Servlet 3.0, Servlet 3.1, Java
So why should we produce asyncronous code? The speaker presented some valid points:
The rest of the session contained a quick walk through of different Java-technologies and short descriptions of the async parts of them. The following technologies were discussed:
@WebServlet(asynchronous=true...)
, AsyncListener
@Asynchronous
, @Suspend
@Asynchronous
Summary: Very interesting topic. The speaker went through the technologies very quickly however. The presentation would have better suited a twice as long session.
Rating: 3 (OK English, OK content)
Speaker: Steve Millidge
The talk consisted of a live coding exercise in which a small webapplication that performed calls to a very slow REST service was enhanced so that calls to the REST service were cached using JCache. The application server used in the example was Payara which is a fork of the Glassfish application server. The Payara application server is supported by the company of the same name. The webapplication consisted of a small Java EE 7 application.
It was explained that JCache (JSR-107) is a specification of an API. To be able to use caching via JCache in ones application, one have use a proper implementation. Out of the box Payara uses Hazelcast, which is an open source implementation of the JCache API.
It turned out that is was suprisingly simple to add caching of the REST requests to the example webapplication. An addition of the @CacheResult
annotation to the method performing the REST request was all there took. To also get updates to work, an addition of the @CachePut
to the method performing the updating REST requests was needed.
The annotations in Java EE 7 related to JCache are: @CacheResult, @CachePut, @CacheRemove and @CacheRemoveAll.
Summary: It was really an eye opener to see how easy it was to add caching to the REST requests. Really good presentation if one is into Java EE 7 development. Of one is not doing Java EE 7 development, one gets rather interested in starting doing it after having seen this presentation...
Rating: 4 (Perfect English, good content)
Speaker: Jaromir Hamala
This session was a sister session to the session described above. Like in the previous session, an exising demo application was altered to include caching using JCache. In this session, however, no annotations were used. Instead the caching stuff was performed by actual API calls.
The session started with a general overview of what JCache (JSR-107) is and what it is not. As I wrote before, JCache is just an API. The actual implementations are made by third party vendors. The speaker made the analogy with JMS, an analogy I think which is most appropriate.
Currently there are four implementations of the JCache API:
The speaker then explained the main classes of JCache:
CacheProvider
- manages the life cycle of CacheManager
:s. CacheManager
- Factory for caches.Cache
- the actual cache.Next the speaker described a feature of JCache that he thought was really useful: entry processors. They provide a way of specifying a piece of code that can be run inside the cache. Why would someone want to do that? The example given was that it was desired to add an amount to an integer kept in the cache. The regular way of doing this would be to retrieve the integer with one API call, add the desired amount to it and writing the new value back to the cache. In a multi-threaded environment, this is not a good approach however, as the program can be interrupted beteen the retrival of the value and the update of the cache. If one is unlucky, another thread may want to perform a similar operation at the same time, and a race condition will result.
Usage of an entry processor avoids these kinds of race conditions. The idea is to define an entry processor that does both the fetching of the value, the addition and the update of the cache. As the entry processor code executes atomically, no race condition will occur. Pseudocode:
EntryProcessor<...>
entryProcessor = (entry, args) ->
{
// Read value from cache.
// Calculate new value by adding amountToAdd == args[0].
// Write new value to cache.
};
int idOfItemToUpdate = ...;
cache.invoke(idOfItemToUpdate, entryProcessor, amountToAdd);
Another JCache concept (which also is present in other cache implementations) is the concept of cache loaders. By using a cache loader, one can prepopulate a cache with values for example retrieved from a database. The cache loader functionality in JCache is located in the (appropriately named) CacheLoader
class.
Analogously, if one wants to writes updates to the cache to another place (e.g. a database), the CacheWriter
can be used.
Next the author described how JCache could be utilized in a Spring environment. It seemed pretty similar to how it is used in Java EE 7.
Some advice was given at the end of the session:
CachingProvider.getCachingProvider()
works only if there are exactly one caching provider on the classpath.As closing words, the speaker said something on what he thought was missing from the JCache specification:
Summary: Fact-filled session containg lots of interesting materials. Really good tutorial on how to program to the JCache API.
Rating: 4 (OK English, excellent content)
Speaker: Rickard Bäckman
In the beginning of the session, the speaker explained that the JVM is a Managed Runtime, i.e. it is the glue between the application and the operating system. This has the advantage that it can monitor the application and optimize its execution in runtime.
The talk focused mostly on which parts the HotSpot compiler consists of and how HotSpot decides which part that should be used to interpret or compile a java program and when. It turns out that the HotSpot compiler contains an interpreter and (at least) two compilers: C1 - the client compiler and C2 - the server compiler. The decision of when to use C1, C2 or neither of them (and thus continue to execute the code interpreted) is made by the interpreter which collects profiling information.
The interpreter is always used the first time a piece of code is to be executed.
The C1 compiler compiles fast and have a low memory footprint. It makes some optimizations and is primarily used for programs for which a quick start up is important.
The C2 compiler is used for long running programs where the actual start up time of the program is not that important. It compiles more slowly than C1 and have a larger memory footprint, but produces more optimized code.
The interpreter can after having collected some profiling information decide to compile a piece of code using the C1 or C2 compilier. The compilers can also decide to switch among one another. Perhaps more suprisingly, the compilers can decide to deoptimize a piece of code by throwing away the compiled version and thus indicating that the interpreter should be used the next time it should be executed.
Summary: interesting, but probably not very useful in the daily work.
Rating: 3 (OK English, OK content)
Speaker: Per-Åke Minborg
The speaker is the creator an ORM product called Speedment and the head of a startup company of the same name.
What the Speedment product ORM does differently is that it tries to keep all the data of the underlying database in (JVM) memory. How does it do that then? It turns out that several strategies can be used to handle this. These among others:
Speedment provides a Java 8-like API for insertion and queries. By the looks of it, the API seems really nice! Configuration is done in Groovy.
What's the downsides then? Well, Speedment is eventually consistent (as many NoSQL-databases), i.e. the C in ACID is not supported at all times.
Quotation: We want to put the Java developer behind the steering wheel.
Some light relief: the speaker illustrates the difference between an in-JVM-memory database and a in-cache-on-DB-server resp. a not-in-memory-at-all-DB by running to the wall of the meeting room and comparing that with running from Göteborg to Borås. In the not-in-memory-at-all-DB case the runner also has to wait in Borås for three days. This was an illuminating illustration!
Summary: Interesting!
Rating: 4 (good English, good content)
The second day provided an interesting keynote in which some stuff that (hopefully...) will be present in Java 9 was presented. The highlight for me, however, was the JCache presentations that I thought was really enlightening. This, however, might have to do that I have implemented a cache in the system I am working with using JBossCache, which is the default caching product in the version of JBoss that we currently use, and that I really would like to switch to something better.
The other sessions that I went to was of course also interesting. It was kind of weird hearing about Speedment an ORM that keeps all its data in memory.
All speakers presented their talks with a fair amount of humor which is a good thing I think.
Just like yesterday, for every row in the schedule matrix, I really wanted to go to at least two of the simultaneous talks and had to pick the one I thought would be the most interesting. It was only the talks on JavaScript that did not really interest me. Most of the times I think I chose the right session to go to. Of course, one can never be certain of course, and I am looking forward to view the recorded videos of the sessions I missed.
Summary: Equally nice day as the first! Focus was JCache.
I'm really pleased with having attended the JDays conference. The sessions I went to was all interesting (some more than others, of course) and I had a wide variety of sessions to choose from. Really nice!
If I have to choose one thing as the singular best thing, I have to choose the Docker workshop. It gave me the tools to kickstart my (hobbyist) work with Docker.
During the conference, I wrote down things I'd like to play around with and stuff that I ought to read. These are the things that I noted:
Puh! That was quite a long list! Luckily I've almost a full year to go through all the items in the list...
Looking forward to attending JDays 2016! ;)
JDays is a two-day conference held in Göteborg annually since 2012 (I think). It is primarily focused on Java and related technologies. Some language agnostic sessions are also held. This year the conference was held at the Clarion Post Hotel, which I must say had really nice facilities.
The conference was split up into distinct 45 minutes sessions separated by coffee or lunch breaks. Two or three parallel sessions were held simultaneously, so one always had to prioritize which session to attend.
The following text is a summary of what I remember from the sessions that I went to. In the end, there is a summary of the first day.
Speaker: Ola Bini, Thoughtworks
The keynote given by Ola Bini touched on a wide range of objects. From programming language philosophies to information security and programmer location. Its main focus, however, was to predict how programming would be done in the future. The speaker talked about some of the things he thinks will form the programmer profession in the future. He gave a lot of examples. Some of these were:
Overall, I think that it was an interesting talk from an interesting person. Ola mixed in a joke or two here and there, and the talk touched (briefly) on many different topics. On the downside, I think that the material presented would better have suited a longer session. Perhaps 50% longer or something like that.
Quotation: "Java is the new Cobol."
Summary: Interesting talk touching on many topics. Not much code was shown.
Rating: 4 (Very good English, good content)
Speaker: Filip Maelbrancke
As I am an hobbyist Android programmer I thought I should attend at least one of the two Android related sessions so I went to the one that appeared first on the schedule. The topic of the session was how one could test ones Android app in various way. Normally testing is not one of my favourite topics, but I must say that I liked this session. It gave an overview of different frameworks that can be used to test an Android app. Both unit test and other types of test was covered.
Some of the tools that were mentioned in the talk were these:
The speaker also talked about design patterns that can be used in Android development. Dependency Injection and Model-View-Presenter were mentioned.
Summary: Interesting session mostly describing frameworks and tools that can help you test an Android app in various ways.
Rating: 3 (OK English, OK content)
Speaker: Niccola Paolucci
As I attended the Docker workshop the previous day, I went to this session to get a repetition of the stuff from yesterday and maybe pick up a new nice trick or two. And that was mainly what I've got.
The session was not primarily targeted at total Docker newbies. The speaker very quickly described his view of what docker is, but that explanation was so brief that if you hadn't played around with Docker before, I think most of the stuff presented was incomprehensible. Luckily, I remembered most of the things that I learned yesterday so I understood most of the things the speaker spoke about.
Niccola presented some advantages of Docker:
Some tips and tricks that I picked up during the session:
apt-get
commands, these could be combined to be on the same separated by the and operator &&.docker image image-name
command.docker image --tree
command could be used.Finally the speaker talked a little bit about some tools that can be used in conjunction with Docker:
Summary: Lots of ticks and tricks for using Docker. Basic knowledge of Docker required.
Rating: 3 (OK English, OK content)
Speaker: Angelika Langer
Angelika spoke about how the performance of the new streams
compare to that of old school for-loops. In summary, streams
:s often executes more slowly if only primitives are involved. If proper objects are used, the difference is less. If the computations could be parallelized, streams
:s could perform better that old-fashioned loops.
Summary: Interesting talk about the performance of streams compared to regular for-loops.
Rating: 4 (good English, good content)
Speaker: Filip Maelbrancke
As I went to Filip's first talk earlier on the day and I liked it, I thought I would go to his next one, which should cover Android security, as well. I was not disappointed.
Filip started by saying that he had this definition of security: security == managing risk.
The Android security model is based on these three pillars:
The speaker then went on to describe a wide range of tools that can be helpful for when working with security on Android.
Some useful techniques to discover if your app has been tampered with:
How to protect in app data (in descending order):
android:allowBackup
to false.Cryptography related libs:
Key management: either ask the server for the cert or store in the Android system area.
Keystore provider: Android Keystore, since 4.3, can be hardware backed.
Nogotofail: tool for testing network stuff.
Many good books on Android security (compared to Andoid testing)! E.g. Android Hacker's handbook.
Summary: Really interesting and useful talk about Android security and what tools that exist to help hardening and testing an app.
Rating: 4 (OK English, good content)
Speaker: Murat Yener/Mert Caliskan
Murat and Mert have written one book each on either Java EE or Spring, so they know a lot about the subject of the session.
The session was set up to be a match between Java EE and Spring to decide which technology is the better one.
The session started with a short description of the different features of Java EE and Spring. The comparison showed that the functionality of the two technologies are rather similar in scope.
Most of the rest of the session was a comparison between two programs constructed to have the same functionality but one written using Java EE and the other using Spring.
One difference that struck me was how much more XML configuration that was needed in the spring version compared to the Java EE version. Otherwise I think the two versions were quite similar.
The smackdown ended in a draw. The conclusion was that the technologies were similar in scope and that they supported roughly the same annotations, but the names of the annotations differed a bit.
It was fairly interesting to witness the Java EE vs Spring smackdown. The authors were humorous and clearly knew their subject very well. The quality of the spoken language was, unfortunately, not perfect, which made the presentation hard to follow from time to time.
Summary: Interesting comparison between Java EE and Spring. Good to know that they are similar in scope. Will be nice to get the slides of the presentation, which will be useful as a translation guide between the annotations of the two technologies.
Rating: 3 (Fairly OK English, OK content)
Speaker: Milen Dyankov
The session consisted of two parts: first a general discussion about what a microservice is and pros and cons of a microservice compared to a monolith, then a live refactoring of an application from a monolithic architecture, via a "standard" microservice architecture to an OSGi microservice architecture.
If the need for a microservice architecture is not obvious and overwhelming, the speaker recommended starting with a monolithic architecture, but where the monolith is divided into modules which, if the need arises, can be refactored to microservices later on.
The speaker recommened two articles on microservices: Microservices by Martin Fowler and James Lewis and Microservices and Jars by Uncle Bob Martin.
The live coding session took as starting point an example from the Java EE Tutorial of Oracle's. The example program was first modified so that a business logic component was added. As that component did not need/should not have to know about persistency details of the entity model, a separate hierarchy of DTO:s duplicating the entity model was created. When that had been done, the business component could then, quite easily, be refactored out to be a microservice. The speaker then proceeded to refactor the component so that it became OSGi compliant. This step did not seem to be particularly hard to perform either, which was kind of nice to see.
As I haven't been into the microservice world very much, it was interesting to get a short introduction to microservices in general. The live coding session was also interesting, but it is often hard to follow (and even more difficult to remember) all the steps as they often are executed quite rapidly. The main take away from the live coding session was therefore: it does not seem very hard so it should be possible to perform the steps at home. Given a sufficient amount of time...
Summary: Interesting talk and live coding session about microservices. Some materials on OSGi as a bonus.
Rating: 4 (good English, good content)
The first day of JDays was filled with interesting talks. It's a pity that one can only be in one place at any given time. Often I would have liked to attend two or all three of the sessions. I prioritized going to the sessions covering Android and Docker, and this I don't regret. The sessions on microservices and Docker was probably the most useful from a job perspective. The session on the performance of streams was very interesting, but maybe not that useful. I think that if you have the oppertunity to use streams (i.e. if you use Java 8), then you should do it because streams offer a cleaner and more expressive syntax. It does not matter if it is a little bit slower than using old-fashioned for-loops, the more expressive syntax more than makes up for that.
Summary: Really nice first day! Focus was Android and enterprise Java.
Io is a prototype based language. What does that mean then? It means that the only way to create an object is to create a clone of another object. The object that is cloned is called the prototype. There aren't any classes in Io. Only objects.
A central concept in Io is a message. A message can be sent to an object (which when receiving a message is called a receiver), which then responds to the message by returning a receiver (itself or another object), which then can be the target of another message and so on. The whole Io language is based on chaining receivers together by means of messages. The object sending the message to the target
is called the sender
.
Another central concept is that of a slot. A slot is similar to an entry in a hash table. It has a name (comparable to the key of the hash table entry) and, possibly, a value. An object can contain an unspecified number of slots and new slots can be added on the fly, so in this sense an object is like a hash table. In fact, there isn't much more to an object than it being a collection of slots. Sounds a little boring? Maybe, but one can accomplish quite a lot with just a set of slots.
So, what can be put in a slot? The most obvious thing to put in a slot is of course some piece of data, e.g. a string. The value of the slot can then be accessed by sending a message containing the name of the slot to the object. But a data value is not all that can be put in a slot. It turns out that also methods can be stored in slots. It is by putting methods in the slots of an object and then sending a message contaning the name of the method to the object that methods are invoked.
An Io program is executed by running the interpreter io
that comes with the Io installation bundle. After having started the interpreter a prompt looking like this will be displayed:
Io 20110905
Io>
Now programs can be entered at the prompt.
Another way of executing an Io program is to give the name of a file containing an Io program as a parameter to the io executable: io myProgram.io
.
Comments can be created using //
, /**/
or #
syntax.
Hello World is as simple as to send the println
message to the "Hello, World!"
object:
"Hello, World!" println
...
Hello, World!
Listing 1: Hello World in Io.
To create an object to start working with, one can be cloned from the Object
prototype. From that object, other objects (both types and other objects) can then be created. To create a type, the name of the clone should start with a capital letter. Regular, non-type objects have names starting with a lowercase letter.
Animal := Object clone
Dog := Animal clone
fido := Dog clone
Listing 2: Cloning objects to create other objects.
Instead of having variables, Io has, as I mentioned above, slots. A slot can be created and assigned using the :=
operator. If a slot already exists, a value can be assigned to it using the =
operator. The ::=
operator is like :=
but in addition to creating a slot and assigning to it, it also creates a setter method for the slot.
When assigning values to slots, the following syntax is used objectOrType nameOfSlot := value
An example:
MyType := Object clone
MyType mySlot := "This is a value for a slot in a type"
MyType mySlot println
myObject := Object clone
myObject slotInObject := "This is a value for a slot in an object"
myObject slotInObject println
...
This is a value for a slot in a type
This is a value for a slot in an object
Listing 3: Assigning values to slots in types and objects..
Something that confused me at first when I was looking at example programs that I found on the net, was assignments to slots where the object or type of he slots wasn't specified. Like this: a_slot := 42
. When I experimented a bit, it turned out that when using that syntax, the type Object
is implicit. I.e. if no object or type is specified when assigning or accessing a slot, the slot is assumed to belong to Object
. This syntax is quite handy for program parts where a regular variable should be used in a conventional programming language. An example:
a := 1
b := 2
c := a + b
"In Object a has the value: " print;Object a println
"In Object b has the value: " print;Object b println
"In Object c has the value: " print;Object c println
...
In Object a has the value: 1
In Object b has the value: 2
In Object c has the value: 3
Listing 4: When no object or type is specified, it defaults to Object.
In listing 4 above, I've used semicolons to separate expression instead of having each expression on a separate line. This was done to group the expressions in a more logical way (and had the nice side effect of showing the usage of ; as an expression separator ;)
Methods are defined by creating a method
object and assigning it to a slot of an object. The name of the slot becomes the name of the method. To invoke the method on the object send the name of the method/slot to the object. An example:
Vehicle := Object clone
Vehicle description := method("Driving...." println)
Vehicle description
...
Driving....
Listing 5: Creating and invoking a no-arg method..
To define a method that takes parameter, specify the names of the parameters as the first arguments in call to method
:
Multiplier := Object clone
Multiplier timesTwo := method(n, n * 2)
Multiplier timesTwo(4) println
Multiplier multiply := method(n, m, n * m)
Multiplier multiply(7, 8) println
...
8
56
Listing 6: Defining methods taking arguments.
As can be seen from the example above, when calling a method, the arguments are specified in a comma separated list enclosed in parenthesis.
A list
is an ordered collection of objects. In Io all lists have List
as (one of) their prototypes. A list can be created by using the lists()
method on Object
:
a_list := list("a", "b", "c", "d")
a_list println
...
list(a, b, c, d)
Listing 7: Creating a list.
The List
class supports a lot of different methods. For example, an element can be added using the append()
method:
a_list append("e")
a_list println
...
list(a, b, c, d, e)
Listing 8: Appending to a list.
A value in a list can be retrieved using the at()
method and it can be set using the atPut()
method.
b_list := list(1,2,3)
b_list at(1) println
b_list atPut(1, 42)
b_list at(1) println
...
2
42
Listing 9: Getting and setting values in a list.
Other methods List
:s support are for example: size()
, pop
, at()
, prepend()
, sum()
, average()
and isEmpty()
.
Io also supports a hashtable like datastructure: Map
. Basic usage of a Map
can be seen in the example below:
aMap := Map clone
aMap atPut("a", "The letter a")
"The element for key a" println
aMap at("a") println
"
Now adding a value for key b" println
aMap atPut("b", "The letter b")
"
Printing the map as a list..." println
aMap asList println
"
...and as an object:" println
aMap asObject println
"
The number of elements in the map is:" println
aMap size println
"
The keys of the map are:" println
aMap keys println
Listing 8: Basic usage of Map.
There are a couple of different ways in which an if clause can be constructed. The first way is to use the if()
construct taking two (no else clause) or three (else included) parameters. The first parameter is the boolean expression on which the if
clause should be based, the other parameter is the message to evaluate if the boolean expression is true
, and the third parameter represents the message to evaluate if it was false. An example:
if(7 < 42, "7 less than 42" println, "7 greater than 42" println)
...
7 less than 42
Listing 10: If clause, variant 1.
The second way to create an if clause is to the if() then() else()
construct. Here the boolean expression goes in the if()
method, the message to evaluate in the true branch in the then()
and the message to evaluate in the false branch in the else()
(which of course is optional). The previous example rewritten to use this construct looks like this:
if(7 < 42) then("7 less than 42" println) else("7 greater than 42" println)
...
7 less than 42
Listing 11: If clause, variant 2.
There also exists an elseif()
construct:
if(87 < 42) then("87 less than 42" println) elseif(87 == 42) then("87 equal to 42" println) else("87 greater than 42" println)
...
87 greater than 42
Listing 12: If clause containing both an elseif() and a else() clause.
The third variant of an if clause is the ifTrue()
and ifFalse
constructs:
(87 < 42) ifTrue("87 less than 42" println) ifFalse("87 greater or equal to 42" println)
...
87 greater or equal to 42
Listing 13: If clause, variant 3.
An important thing to notice is that the code to execute in one of the branches does not have to fit in a single line. An (almost I suppose...) unlimited number of messages can be constructed by separating them using semicolons or by putting them om separate lines. An example:
if(7 < 42,
"We all know that 7 is less than 42..." println
"...but I want to stress the fact anyway." println
)
...
We all know that 7 is less than 42...
...but I want to stress the fact anyway.
Listing 14: If clause with a multi-message code section in its true branch. Messages separated by a newline.
The program above could equally well have been written using the message separator ;
if(7 < 42,
"We all know that 7 is less than 42..." println ; "...but I want to stress the fact anyway." println
)
...
We all know that 7 is less than 42...
...but I want to stress the fact anyway.
Listing 15: If clause with a multi-message code section in its true branch. Messages separated by a semicolon.
When using the if clause with an else part and where the true/false sections contain multiple messages separated by ; one has to be extra cautious not to mix up the comma separating the true branch from the false branch and the semicolons separating the messages. In these situations, where both the true branch and the false branch contain multiple messages, it is probably better to separate the messages with newlines instead of semicolons. When I think about it, it is probably almost always better to separate the messages with newlines instead of semicolons ;)
Io supports the following looping constructs:
loop
: loops infinitely.repeat
: loops a specified number of times. for
: loops over an integer interval, possibly with a step.while
: loops until a boolean expression evaluates to false.
i := 0
loop(
i println
i = i + 1
if(i>=3, break)
)
...
0
1
2
Listing 16: Example of using loop.
In the example above, we also see the usage of break
, which, as usual, breaks out of the innermost loop. Io also supports continue
.
3 repeat("Hello!" println)
...
Hello!
Hello!
Hello!
Listing 17: Example of looping using repeat.
for(i, 1, 10,
i println
)
...
1
2
3
4
5
6
7
8
9
10
Listing 18: Example of the for construct not using an explicit step (=> step of 1 is used.).
An explicit step can also be specified for for
loops.
for(i, 1, 10, 2,
i println
)
...
1
3
5
7
9
Listing 19: Example of a for loop having an explicit step.
i := 0
while(i < 3,
i println
i = i + 1
)
...
0
1
2
Listing 20: Example of a while loop.
As was told before, message passing in Io is build on sender
:s sending messages to targets
that executes the message. The retrieve metadata about a message, the sender of the message or the receiver of the message the call
function can be used.
call
can be used in these forms (among others):
call sender
- retrieve the sender the message currently being processed.call target
- retrieve the target the message currently being processed.call message name
- retrieve the name of the message currently being processedcall message arguments
- retrieve the arguments of the message currently being processed.
An example program to demonstrate the usage of call
can be seen in the listing below:
# Sets up a chain of three objects: right - middle - left, where a message is sent from
# the right hand object, through the middle object to the left hand object.
# The purpose is to illustrate the usage of the call function to retrieve metadata about
# the sender, target and the message itself.
# Create the objects.
rightHandObject := Object clone
middleObject := Object clone
leftHandObject := Object clone
# Create a method that should receive the message and print it.
leftHandObject receive := method(arg,
"LHO received: " print
arg println)
# The middle object should forward the message after having used call to print metadata about it.
middleObject objectToSendTo := leftHandObject
middleObject passThrough := method(txt,
"*** In MiddleObject.passThrough" println
"*** Sender" println
call sender println
"*** Target" println
call target println
"*** messge name" println
call message name println
"*** messge args" println
call message arguments println
self objectToSendTo receive(txt))
# The right hand object should start the message chain.
rightHandObject objectToSendTo := middleObject
rightHandObject send := method(txt,
self objectToSendTo passThrough(txt))
# Initially, we print some info about the objects so that we can compare the hashcodes of the
# objects with the metadata printed by the middle object. This way we can figure out which
# objects that are returned by call sender and call target.
"***********" println
"** RHO: " println; rightHandObject println
"** MiddleObject: " println; middleObject println
"** LHO: " println; leftHandObject println
"***********" println
# Start the message passing by sending a message to the right hand object.
textMessage := "Hello"
"Sending " print; textMessage print; " to rightHandObject" println
rightHandObject send(textMessage)
Listing 21: Using call to retrieve metadata..
Output from a run of the program can look like this:
***********
** RHO:
Object_0x24e41d0:
objectToSendTo = Object_0x241ed40
send = method(txt, ...)
** MiddleObject:
Object_0x241ed40:
objectToSendTo = Object_0x241ed00
passThrough = method(txt, ...)
** LHO:
Object_0x241ed00:
receive = method(arg, ...)
***********
Sending Hello to rightHandObject
*** In MiddleObject.passThrough
*** Sender
Object_0x24e41d0:
objectToSendTo = Object_0x241ed40
send = method(txt, ...)
*** Target
Object_0x241ed40:
objectToSendTo = Object_0x241ed00
passThrough = method(txt, ...)
*** messge name
passThrough
*** messge args
list(txt)
LHO received: Hello
Listing 22: Output from the program illustrating the usage of call.
We see that, as expected call sender
returns the right hand side object and call target
return the middle object (which is executing the call target
). By using call message name
and call message arguments
we can retrieve the name of the called method (passThrough
) and its arguments.
Object reflection involves examining the slots and the prototypes of an object (or type). The slots of an object can be retrieved by sending the slotNames
message to the object. Which prototypes an object has can be found out using the proto
message.
The following example demonstrates how slotNames
and proto
can be used.
# Add a method that recursively prints the names of the slots of this type and its prototypes to Object. When the recursion reaches the level just before Object it is stopped.
Object printSlots := method(
# Print the slots of this object
"The slots of " print
self type print
" are: " print
self slotNames foreach(slotname,
slotname print
", " print
)
"" println
# Recursively call printSlots() for all prototypes.
# The prototype of Object is Object => we have to check
# if the prototype is Object to avoid infinite recursion.
if (self proto != Object, self proto printSlots())
)
# Create a new type having one slot containing data and one slot containing a method.
MyType := Object clone
MyType mySlot := "This is a value for a slot in a type"
MyType aMethod := method("This is a method" println)
# Create another type by cloning the first type.
AnotherType := MyType clone
# Print the slots of the second type (which recursively will print the slots of its prototypes (minus Object)).
AnotherType printSlots()
Listing 23: Usage of slotNames and proto.
The concurrency support in Io seems rather nice. Let's delve a little bit into that.
A coroutine is a function that voluntarily suspends, or yields, its own execution to let some other piece of code (which most likely is a coroutine as well) run. When the other code piece has finished, the function resumes its execution and runs until it terminates or yields again. This way, the execution can switch back and forth between a number of functions that will cooperate to produce the final result of the program.
A function in Io can yield the execution by using the yield
keyword.
Another piece of the Io language that one has to know about when playing around with coroutines, is that messages can be sent asynchronously to an object by prepending either @ or @@ to the name of the message when sending it. The difference between @@ and @ is that the former returns nil
and the latter a future (see the section on futures below). If a message is sent synchronously to a coroutine, there will be no other coroutine executing when yielding, so one has to use asynchronous message passing when playing around with coroutines. Let's try it out.
The following is a small program containing two objects each having a method that writes two strings to standard out. Between the two write statements, there is a yield
that lets another concurrently executing method have a go. In the first version of the program, we invoke the two methods synchronously:
man1 := Object clone
man1 talk := method(
"Man 1 says hello!" println
yield
"Man 1 says nice to meet you" println
)
man2 := Object clone
man2 talk := method(
"Man 2 says hello!" println
yield
"Man 2 says nice to meet you" println
)
man1 talk; man2 talk
...
Man 1 says hello!
Man 1 says nice to meet you
Man 2 says hello!
Man 2 says nice to meet you
Listing 24: Synchronous invocation of coroutines. Not much point in doing this.
As we see, the output is as expected: first the first function executes, then the other function executes.
If we change the invocation to be asynchronous however, the result is different:
man1 := Object clone
man1 talk := method(
"Man 1 says hello!" println
yield
"Man 1 says nice to meet you" println
)
man2 := Object clone
man2 talk := method(
"Man 2 says hello!" println
yield
"Man 2 says nice to meet you" println
)
man1 @@talk; man2 @@talk
Coroutine currentCoroutine pause
...
Man 2 says hello!
Man 1 says hello!
Man 2 says nice to meet you
Man 1 says nice to meet you
Listing 25: Asynchronous invocation of coroutines. Interleaved execution.
Now the execution of the two methods were interleaved. We also see that in this particular execution man2 gets to execute first, but this is just a coincidence. It could equally well have been the other way around.
We've used the Coroutine currentCoroutine pause
to make the program wait until all asynchronous methods have finished executing. Without it, the main program could have terminated first, before the two methods, and then there would have been no output, which is kind of boring.
An actor is a concept used in computer science to model a concurrent entity. When receiving a message, an actor can perform business logic (e.g. change its own state), create more actors and send messages to other actors. What it cannot do is to change the state of other actors. It communicates with other actors by sending messages to them. This is the advantage that using actors instead of threads for concurrent programming. Threads may share state, which may lead to all kinds of problems, eg. race conditions and deadlocks.
So how does one create an actor in Io? By sending a concurrent message to a regular object! Simple, isn't it? Let's exemplify.
We create two objects each having a method that waits for a certain amount of time before printing a message. When we call the messages synchronously, we see that as expected, the methods execute in order:
slower := Object clone
slower run := method(
wait(2)
writeln("In slower")
)
faster := Object clone
faster run := method(
wait(1)
writeln("In faster")
)
slower run(); faster run()
...
In slower
In faster
Listing 26: Invoking synchronously => regular object.
When we invoke the methods synchronously, however, the two objects become actors, and the faster object terminates its execution more quickly than the slower one:
slower := Object clone
slower run := method(
wait(2)
writeln("In slower")
)
faster := Object clone
faster run := method(
wait(1)
writeln("In faster")
)
slower @@run();faster @@run()
wait(3)
...
In faster
In slower
Listing 27: Invoking asynchronously => actor.
A future is an object returned from an asynchronous invocation of a method. The future is returned from the method directly when the method starts executing not, as a regular result object, in the end of the method when its execution is about to end. A future contain a synchronous method to retrieve that final answer, however. An invocation of that will not return until the original method has completed its work and returned the result. This means that a program can invoke a method, store the future returned by that method, perform some other task and, when that task is finished, retrieve the result computed by the method by invoking the synchronous method on the future. This way the program can perform useful work at the same time as the method executes instead of just waiting for the method to terminate. Smart, isn't it?
An example:
futureResult := URL with("http://www.larsnohle.se") @fetch
writeln("This will execute at once")
# Perform useful work here!
resultSize := futureResult size
writeln("Has got the result. The size of it was: ", resultSize)
...
This will execute at once
Has got the result. The size of it was: 1838
Listing 28: Using a future to retrieving the result of an asynchronous method call.
The message This will execute at once will be printed right away. Then some time will pass before the web page will be retrieved and the second message will be printed. In this program no useful work is performed after the call to @fetch, but in a real world program some calculation could have been performed here.
Io, of course, contains many other features as well. One of them is support for metaprogramming by redefining the forward
message of Object
.
forward
can be use to pick up messages to an object that the object normally wouldn't handle. As an example, we write a small program that redefines forward
so that the name of the non-existing message will be printed, and so will also any string arguments of that message.
Greeter := Object clone
Greeter forward := method(
write("The name of the message is: ")
call message name println
call message arguments foreach(arg,
content := self doMessage(arg)
if (content type == "Sequence",
write("Found a string argument: ")
content println)
)
)
greeter := Greeter clone
greeter say("Telefonbanken")
...
The name of the message is: say
Found a string argument: Telefonbanken
Listing 29: Redefining forward
Here are some things that tricked me while writing some example programs in Io:
=
instead of :=
if
clause using commas instead of newlines.My, non-optimal, implementation of the FizzBuzz kata in Io looks like this:
for(i, 1, 100,
if(i % 3 == 0,
"Fizz" print
)
if(i % 5 == 0,
"Buzz" print
)
if(i % 3 != 0 and i % 5 != 0,
i print
)
"" println
)
Listing 30: FizzBuzz implementation in Io.
Io is an interesting language. The syntax is strange until you get the hang of it. I've never programmed in a language that is anything like it. This makes it rather fun to use Io. It feels a little bit like you are playing an adventure game trying to explore a world that has just been discovered. It's great fun!
I also like the smallness of the language. It seems that the core language is quite small. There are a very limited number of language constructs that one has to learn. Once you have learned those, I think that the language will not give you very many suprises regarding its syntax.
On the technical side, I like the concurrency support of Io. It it extremely easy to send an asynchronous message instead of a synchronous message to an object (just prepend @ or @@ to the message name). Prototype based inheritance is also interesting. I'm not sure that I like it really, but it is something...different, which makes it interesting. At least in the beginning.
It is quite hard to find information about Io. There are the official home page, which contains, how shall I put it, brief descriptions about most of the Io syntax. The problem is that the descriptions are so brief that if one wishes to do something just a little bit out of the ordinary, the chances are that that isn't covered by the documentation. Then the second problem kicks in: it is not easy to find any other resource regarding Io on the Net. Googling for an Io related topic doesn't will not give you very many hits. And the hits found will probably be either links to the official home page, or to some blog where a solution to one of the exercises in the Seven Languages in Seven Weeks book are discussed (which of course is helpful if you are struggling with with those...)
The time it takes to write these blog entries about the "14" languages takes more time than I had anticipated. Something always comes up that I have to do before I can continue reading/trying out the language/writing about it: work, life, another topic to write about. Therefore I think I have to revise my original plan of going through all 14 languages in the first six months of 2015. My aim now is going through the first seven languages in the whole of 2015. I slight modification of the plan... But hopefully it is more realistic. If I get around to doing more than seven, it's good, but I won't be disappointed if I don't. Well, well. We'll so how it goes.
Now on to the next language: Prolog! But first it seems that I have to study up a little bit on Docker...