Computer Science Canada An Introduction to Io |
Author: | wtd [ Sun Jan 01, 2006 6:06 pm ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Post subject: | An Introduction to Io | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Why another programming language? We've all heard the expression "think outside the box." It means that you shouldn't allow your thinking to be limited by what you're accustomed to. Every single programming language forces us to think inside a box. Some of the boxes are smaller, and some are larger, but they're still boxes. The only way to gain a broad understanding of programming then, is to learn as many programming languages as we can. Why this programming language? Students stumble over two things when learning a programming language. They either have trouble with the syntax or the semantics. How the language looks, or how it behaves. Syntax is not without importance. Finding easier ways to do things is good. But syntactic conveniences are generally just a sign of support for beneficial semantics. In functional programming languages we don't say the ability to pass around anonymous functions is useful because of the syntax.
This is useful because of the utility of being able to quickly generate functions and pass them around as values. It is an idea we can use elsewhere to our advantage. The syntax we'll rarely be able to use. Io is a programming language with minimal syntactic rules. There is very little to trip over because there just isn't much there. This emphasizes semantics. So what the heck is Io? Io is a purely object-oriented programming language. Everything in Io is an object. What does that mean? Well, simply put, it means the language is consistent.
A big plus is that Io features an interactive interpreter, making it easy to experiment with code. And that probably doesn't tell you a whole lot, so let's be a bit more specific. Hello, world
What's going on here? Well, "writeln" is a message. A message has a receiver, and optionally, arguments. At the top level of a program, the receiver is implicitly Object. Therefore we can easily write the following instead.
The argument in this case is the string, "Hello, world". When the "writeln" message is sent to Object, it looks up the corresponding method and invokes it with the arguments supplied. Message passing is not so obvious until you see the same action performed as follows.
What about math and stuff? Operators exist, and are quite handy for all of our mathematical needs. They also follow the same precendence rules you're used to.
So, how does this mesh with the idea of messages and consistency? Well, let's rewrite the above.
It's just kind of ugly to do it that way. Variables? It's not unusual to want to give names to pieces of information.
But what's really going on here?
As with writeln, Object is implicitly there. Thus "msg" becomes just a value tied to a "slot". A receiver other than Object?
Here msg is the receiver, and println the message. Of course, the implicit Object is still in there.
Perhaps this is easier to understand as follows.
The println message is being sent to "Object msg". Get used to the version without parentheses, thogh, since that's what you'll see from here on out. Create your own method So far we've only seen a very few standard library provided methods. Let's create one.
There's nothing new here. The := and method are both messages. The following is a more verbose version.
Let's add an argument.
What if the method has lots of arguments?
So, what's going on here? What is "thisMessage"? That refers to well, the current message. In the case of:
It refers to that passing of the sayHelloTo message. The arguments slot of that object contains a list which holds the arguments. It's fairly straightforward. For that list, we send the "foreach" message. For each argument in that list, evaluate the message. Of course, here we're sending two arguments to "foreach", so the first is the name we're giving to each argument in the list. So, why can't we just write the following?
The arguments in the list are not simple values. They're actually objects which represent the messages themselves, before being evaluated. To evaluate those messages and get the resulting value, we can use the doMessage message. How it all comes together is reasonable straightforward. A conditional What if we want it to print "Hello world" if no arguments were passed in?
Here we can see first off the addition of the "args" slot to give a more convenient name to the arguments list. The next new thing of course is the conditional. If there are no arguments to sayHelloTo, then we first print "Hello world" and then we send the return message with the argument Nil. The return message causes control flow to skip to the end of the method. It's interesting to note that multiple messages here are considered as a single message by the if message. They are separated by a newline, but could be separated by a semi-colon, as the code after "==>" shows. Now here we're only providing two arguments to "if". We could provide three.
Here the third argument becomes the "else" part of the conditional. An explicit return is no longer necessary since only one of the two messages can be evaluated. Evaluating messages and a greater appreciation of "if" Let's implement our own "if" message.
It replicates "if" reasonably well, and it's quite simple. So how does this work? It wouldn't work in many other languages. They strictly evaluate expressions (the rough equivalent of "messages"). If something is passed as an argument to a method/function/etc. it is immediately evaluated, and any side-effects become immediately apparent. That is something we can't have with a conditional. It has to evaluate only one argument or the other. Fortunately Io makes this simple. Writing our own control structures is therefore a piece of cake. More flexible conditionals The conditionals we've seen so far are just peachy... if we want a very simple decision between two possibilities. It looks less nice when we have lots of possibilities.
You will note that I didn't just copy and paste this from the interpreter. Instead, I save this in a file named "hello.io", and run it from the command-line with "io hello.io". Alternatively, you can open your interpreter and send the following message.
This will run the file. If you've created new methods in that file, they will become available later on in the interpreter. Adding slots to something other than Object So far all we've done is give new variables and methods to Object. We could try adding to something else.
But how then did "Number" come into being? Number is just a clone of Object. It then had a bunch of extra stuff tacked on. But, that stuff was tacked onto Number, and not Object. For instance, + is valid for Number, but not for Object.
And yet, Object is a prototype of Number, so Number responds to all of the messages Object does. Those already familiar with object-oriented programming should be thinking "inheritance" about now. But let's create something new.
That looks messy. Let's use the "do" message to clean it up.
Now, we'll want to create new Name objects.
The myName object has as a prototype Name, and thus responds to the fullName message. Now, what if we want a more formal name?
Since Name is a prototype of FormalName, we can use the resend message to send the fullName message to Name which gets the combination of the first and last names. We then simply add the title onto the beginning of that. All sorts of prototype shenanigans So, what if I have:
And I want to be able to treat this as a FormalName?
So, what exactly did I do? I added a title slot to myName, and I prepended FormalName onto the list of prototypes for myName. Then, when I sent the fullName message to myName, instead of looking in Name for fullName, it found a perfectly suitable version in FormalName and used that instead. I convinced myName that it is in fact a FormalName. Operators So, let's say we want to check to see if two names are equal.
Then defining the opposite is a simple matter of simply checking to see if they are not equal. If == returns Nil, then they are not equal.
|
Author: | Cervantes [ Sun Jan 01, 2006 6:37 pm ] | ||||
Post subject: | |||||
wtd wrote: What if the method has lots of arguments?
Forgive me if I'm using the wrong terms here, but here foreach is given two arguments, which seems to contrast with how you used foreach at the beginning:
Is this an example of optional parameters? A nice introduction to Io. Thanks wtd. ![]() |
Author: | wtd [ Sun Jan 01, 2006 6:39 pm ] |
Post subject: | |
When the foreach message is sent with three arguments, the first acts as the index. |
Author: | Cervantes [ Sun Jan 01, 2006 6:43 pm ] |
Post subject: | |
![]() I got that from the output of the first example. It's a little odd, though, in that the supposed "optional" parameters are taken from the start, though that must be a direct consequence of having the "body" of the "function" (for lack of my Io-terminology) as a parameter. |
Author: | wtd [ Sun Jan 01, 2006 7:19 pm ] |
Post subject: | |
The arguments are stored in a list each time the message is sent. You can do anything you want with this list at runtime. |
Author: | wtd [ Sun Jan 01, 2006 9:25 pm ] |
Post subject: | |
Oh yes, and for more information: http://www.iolanguage.com |
Author: | Naveg [ Sun Jan 01, 2006 10:27 pm ] | ||
Post subject: | |||
wtd wrote:
Why, in the first example, does the method return "Bob" and not "true", "1" or "Bob Smith" |
Author: | rizzix [ Sun Jan 01, 2006 11:06 pm ] |
Post subject: | |
prototype based and minimilistic. ![]() ![]() |
Author: | wtd [ Mon Jan 02, 2006 12:34 am ] | ||||||
Post subject: | |||||||
Naveg wrote: wtd wrote:
Why, in the first example, does the method return "Bob" and not "true", "1" or "Bob Smith" Hopefully the following will help demonstrate:
And to demonstrate the precendence:
|
Author: | wtd [ Mon Jan 02, 2006 1:36 am ] | ||
Post subject: | |||
The only thing that's "false" is Nil. Thus a comparison like "4 == 4" doesn't return "true", it returns 4. 4 is true, and keeping that information around can't hurt.
|
Author: | wtd [ Mon Jan 02, 2006 1:48 am ] | ||
Post subject: | |||
rizzix wrote: prototype based and minimilistic.
![]() ![]() I'm surprised you're so jazzed about a language that allows for the creation of new operators. ![]()
I'm also a bit surprised you didn't have a stroke when you saw this. |
Author: | Naveg [ Mon Jan 02, 2006 2:10 am ] | ||
Post subject: | |||
wtd wrote: The only thing that's "false" is Nil. Thus a comparison like "4 == 4" doesn't return "true", it returns 4. 4 is true, and keeping that information around can't hurt.
Ah, now i understand ![]() |
Author: | wtd [ Mon Jan 02, 2006 3:36 am ] |
Post subject: | |
Naveg wrote: Ah, now i understand
![]() Excellent! |
Author: | rizzix [ Mon Jan 02, 2006 8:09 am ] |
Post subject: | |
wtd wrote: I'm surprised you're so jazzed about a language that allows for the creation of new operators. I did. But i decided to ignore it for now. It was the only way i could come up with a "nice" reply. But then again, if they didn't add operator overloading, it would go against Io's philosophies of being consistent.. soo.. yea. It basically boils down to rigorous-prevention-of-abuse over consistancy, and they chose consistancy.
![]() wtd wrote: I'm also a bit surprised you didn't have a stroke when you saw this.
I guess prototypes call for Mixins done right ![]() |
Author: | Drakain Zeil [ Mon Jan 02, 2006 10:45 am ] |
Post subject: | |
This looks interesting. I'll tack it onto my things to learn list. |
Author: | wtd [ Mon Jan 02, 2006 3:02 pm ] | ||
Post subject: | |||
Mixins? Ask and ye shall receive. ![]()
|
Author: | wtd [ Mon Jan 02, 2006 6:07 pm ] |
Post subject: | |
A list of prototype-based languages. http://www.dekorte.com/docs/protos/ |