Python vs. Io

D

Daniel Ehrenberg

Io (www.iolanguage.com) is a new programming language that's purely
object-oriented (but with prototypes), has a powerful concurrency
mechanism via actors, and uses an extremely flexible syntax because
all code is a modifiable message tree. Like Python, it is dynamically
typed, has a very clean syntax, produces short code, and is
excruciatingly slow (on the same level as eachother). Io has a unique
syntax combining Lisp's idea of functions for flow control with
traditional function syntax and smalltalk-like syntax to get slots of
objects. Io has no keywords and everything, even multiple lines of
code, can be used as an expression. Even as a beginner to Io, I was
able to write something in just 43 lines to let you do something like
this (and more) in Io:

each((key, value) in map(x=1, y=2, z=3),
write(key, ": ", value, "\n")
)

Neither the each function nor the map function were built in (Io has
other mechanisms for that kind of thing, but they are less efficient).
Can anyone show me how to so much as alias "for" to "each" in Python
or allow block syntax at all without hacking the C source code?

Io doesn't use __methods_with_this_annoying_syntax__ because nothing
is supposed to be for internal use only and there are only about three
functions that would cause a problem if overridden.

For embedding, Io doesn't have to use Py_ALL_CAPS, instead it just
uses IoCamelCase, which looks much better. Interfaces to C are much
more object oriented.

Many users of Io (myself included) have switched over from Python for
these reasons.

I guess there are still the obvious advantages of Python over Io,
including
*large community
*more bindings to stuff
*strict coding conventions
*inflexible so everything is the same
*no need to close blocks
But unless there are other problems, the first two go away soon
(considering that Io was first created just two years ago). The last
three are somewhat trivial and may even be to Io's advantage.

Daniel Ehrenberg
 
J

John Roth

Can anyone show me how to so much as alias "for" to "each" in Python

Without getting into the language wars, I think what you're looking
for is called the Visitor Pattern (Design Patterns: GOF). It's actually
trivial to implement as long as you're not looking for built-in support.

Something like this works quite well in any object where you want
to implement a visitor over a collection:

class aCollection:
def visit(self, instance):
for x in self._collection:
instance.visitor(x)
return instance.result()

The final call is a little more than the bare visitor pattern, but it
allows the visitor instance to pass back a result so you can use
it in expressions.

A visitor then looks like this:

class fubar:
def __init__(self):
self.whatever = 0
def visitor(self, value):
self.whatever += value
def result(self):
return self.whatever

and you use it like this:

ohWow = aCollection.visit(fubar())

Unlike a block in, for example, Ruby, a visitor instance
can be pre-initialized, it can have multiple methods and
it can persist after being passed against the collection.

Of course, you can use more complex data structures as well,
see the compiler stuff for examples of the visitor pattern used
over the Python Abstract Syntax Tree.

The visitor pattern is also very useful for file system
navigation; I use it consistently.

John Roth
 
B

Brian Kelley

John said:
Without getting into the language wars, I think what you're looking
for is called the Visitor Pattern (Design Patterns: GOF). It's actually
trivial to implement as long as you're not looking for built-in support.

I think Daniel's example is a little bit more complicated than that. It
resembles more closly the lisp-ish macros that were discussed to death a
while ago.

Note that in his code:
each((key, value) in map(x=1, y=2, z=3),
write(key, ": ", value, "\n")
)

key and value are variable names that get used inside the function call.

Here is my pythonic(?) version, however it requries a lambda to bind the
variable names.

def map(**kw):
return kw.items()

def each(seq, func):
for v in seq:
apply(func, v)

from sys import stdout
write = stdout.write

each(map(x=1,y=2,z=3),
lambda key, value: write("%s: %s\n"%(key, value)))

Brian
 
D

David M. Cooke

At said:
Io (www.iolanguage.com) is a new programming language that's purely
object-oriented (but with prototypes), has a powerful concurrency
mechanism via actors, and uses an extremely flexible syntax because
all code is a modifiable message tree. Like Python, it is dynamically
typed, has a very clean syntax, produces short code, and is
excruciatingly slow (on the same level as eachother). Io has a unique
syntax combining Lisp's idea of functions for flow control with
traditional function syntax and smalltalk-like syntax to get slots of
objects. Io has no keywords and everything, even multiple lines of
code, can be used as an expression. Even as a beginner to Io, I was
able to write something in just 43 lines to let you do something like
this (and more) in Io:

each((key, value) in map(x=1, y=2, z=3),
write(key, ": ", value, "\n")
)

Neither the each function nor the map function were built in (Io has
other mechanisms for that kind of thing, but they are less efficient).
Can anyone show me how to so much as alias "for" to "each" in Python
or allow block syntax at all without hacking the C source code?

Hey, if I wanted to to, I could add functions and all that that would
make Python look like Lisp, or IO, or whatever.

But, why? If I wanted to write Lisp or Io, I'd use those.

Your example I'd write in Python as

for key, value in dict(x=1, y=2, z=3):
print '%s: %s\n" % (key, value)

I didn't have to write 43 lines of support code, and it's also two
lines. I don't think "this isn't builtin" is a selling point -- you
need a critical mass of builtins to make a language useful.
Io doesn't use __methods_with_this_annoying_syntax__ because nothing
is supposed to be for internal use only and there are only about three
functions that would cause a problem if overridden.
For embedding, Io doesn't have to use Py_ALL_CAPS, instead it just
uses IoCamelCase, which looks much better. Interfaces to C are much
more object oriented.

Ok, these two points are window-dressing: minor spelling and
punctuation issues (which seems to be what most language wars are about).

Heck, use boost::python for C++ interfaces; those function names are
even shorter. Or use pyrex to generate wrappers, writing them in a
Pythonesque language.
Many users of Io (myself included) have switched over from Python for
these reasons.

I guess there are still the obvious advantages of Python over Io,
including
*large community
*more bindings to stuff

Yep. That's a *big* difference, I'd say.
*strict coding conventions
*inflexible so everything is the same

Can you elaborate a bit on why Python is inflexible? I find Python to
be extremely flexible.
*no need to close blocks
But unless there are other problems, the first two go away soon
(considering that Io was first created just two years ago). The last
three are somewhat trivial and may even be to Io's advantage.

Python is 14 years old, and it's still working on global domination;
Io's got some catching up to do :)
 
C

Christopher Koppler

At some point, (e-mail address removed) (Daniel Ehrenberg) wrote:
[snip-a-lot]
For embedding, Io doesn't have to use Py_ALL_CAPS, instead it just
uses IoCamelCase, which looks much better. Interfaces to C are much
more object oriented.

Ok, these two points are window-dressing: minor spelling and
punctuation issues (which seems to be what most language wars are about).

Heck, use boost::python for C++ interfaces; those function names are
even shorter. Or use pyrex to generate wrappers, writing them in a
Pythonesque language.
Many users of Io (myself included) have switched over from Python for
these reasons.

I guess there are still the obvious advantages of Python over Io,
including
*large community
*more bindings to stuff

Yep. That's a *big* difference, I'd say.

Using any language (other than a Lisp) which is sufficiently powerful
mostly comes down to personal preference regarding syntax. Library and
community support will of course grow for new languages if enough
people find it 'fits their minds' better than anything previously
available.
Can you elaborate a bit on why Python is inflexible? I find Python to
be extremely flexible.

If inflexibility means not being able to arbitrarily change the
syntax, then I'm all for it, because it does help consistency and
readability, which I like very much in my programs, especially when
not working on them alone... Python seems to have found a good middle
ground between strictness and dynamism.
If I wanted a 'flexible' language, I'd use Lisp, or Forth.
 
E

Erik Max Francis

Daniel said:
Neither the each function nor the map function were built in (Io has
other mechanisms for that kind of thing, but they are less efficient).
Can anyone show me how to so much as alias "for" to "each" in Python
or allow block syntax at all without hacking the C source code?

No, you cannot introduce new special forms (i.e., statement syntax) in
Python without modifying the Python interpreter itself.

There are two major camps in language design. One says that you should
be able to modify the language itself to your will, and the other says
that this gets rapidly confusing and you shouldn't. They're simply
different camps, and the two camps won't agree. Furthermore, the
ability to convert a language from the second camp to the first is not
without difficulty; such things usually have to be designed into the
language from the scratch, or you end up with a very complicated process
of creating hygienic macros.

This is a situation where Io is in the first camp and Python is in the
second. They simply don't have equivalent goals; Python emphasizes
transparency and readability more than customizability and flexibility,
whereas Io does the opposite.
Io doesn't use __methods_with_this_annoying_syntax__ because nothing
is supposed to be for internal use only and there are only about three
functions that would cause a problem if overridden.

For embedding, Io doesn't have to use Py_ALL_CAPS, instead it just
uses IoCamelCase, which looks much better. Interfaces to C are much
more object oriented.

These are really stylistic objections, as are the other objections you
list below. That's completely fine, of course; you should choose a
language that jives with your personal sense of language style. But
language designers need to make decisions, and those decisions are
necessarily going to eliminate some styles of programming, while
emphasizing others.

You're in a situation where it sounds like Io jives with your sense of
style more than Python. That's perfectly fine, and I know from my own
research that Io is a neat little language (though I haven't really used
it for anything substantial yet). But you can't expect other languages
to bend to your personal will and personal sense of style, particularly
when it means necessarily violating someone else's corresponding senses.

Io and Python have quite different fundamental approaches to language
design, and so it's not surprising that there are going to be
irreconcilable differences.
 
P

Paul Prescod

Daniel said:
Io (www.iolanguage.com) is a new programming language that's purely
object-oriented (but with prototypes), has a powerful concurrency
mechanism via actors, and uses an extremely flexible syntax because
all code is a modifiable message tree.

I long to live in a world where Python is considered a crufty incumbent
legacy language that is forced on unwilling programmers by Pointy Haired
Bosses. First, it would mean that Python has vanquished languages that
programmers like less. Second, it would mean that the up-and-coming
languages are so unbelievably cool and elegant that they make Python
look like a lumbering dinosaur.

Thanks for reminding me that that that day was once unfathomably far in
the future and now seems almost around the corner!

But when I look closer at IO it seems to me that the day is not as near
as I hope. If you wish to hasten I urge you to:

* finish the IO tutorial
* distribute windows binaries of IO
* make IO compilers to C and Java available
* make bindings to popular windowing toolkits
* make bindings to Java, .NET, COM, SOAP, XML-RPC etc.
* use IO in a production context so that the rest of us can have faith
in its stability
* implement MySQL and Oracle bindings
* publish some books on IO
* point me to some documentation on how to launch and kill processes in IO

If this were all done tomorrow I might be tempted to jump over to IO but
I would be amazed if it were all done even two years from now.

Also, an observation: IO's syntactic simplicity looks to me to be both a
blessing and a curse.

Paul Prescod
 
J

Jonathan Daugherty

As other posts have indicated, this is not the io-user-list. While
you probably have good, purely academic intentions with your post,
this is not the correct forum. Create an io-vs-python list somewhere
and I'm sure you'll get a few subscribers. :)

--

Jonathan Daugherty
http://www.cprogrammer.org

"It's a book about a Spanish guy called Manual, you should read it."
-- Dilbert
 
S

Sean Ross

Hi.
I took a look at Io because of your post, and I have a question:

Are there prototype(?) _and_ instance methods/slots?

In other words, is there a shared state mechanism between instances of a
prototype?
If so, could you show an example.

Thanks,
Sean
 
D

Daniel Ehrenberg

Hey, if I wanted to to, I could add functions and all that that would
make Python look like Lisp, or IO, or whatever.

But, why? If I wanted to write Lisp or Io, I'd use those.

Your example I'd write in Python as

for key, value in dict(x=1, y=2, z=3):
print '%s: %s\n" % (key, value)

I didn't have to write 43 lines of support code, and it's also two
lines. I don't think "this isn't builtin" is a selling point -- you
need a critical mass of builtins to make a language useful.

I know. Maybe in the future, it can be builtin, but it just shows how
flexible Io is. Io has its own version of a for loop (in addition to
being able to iterate directly over a dictionary or list) which in
some cases makes more sense than Python's. It's like this:

for(x, 1, 10, #goes through x with values 1-10
write(x, "\n")
)

IMHO, this makes more sense than iterating over a list created just
for a specific loop. On my computer, Io's loops are faster than
Python's (probably because they are lazy).
[stuff about size of communities and bindings to stuff]

Yep. That's a *big* difference, I'd say.

But the community will grow larger in time. The Io community is bigger
than the Python community two years after Python was released
(although that's probably because it wasn't publicly released for two
years).
Can you elaborate a bit on why Python is inflexible? I find Python to
be extremely flexible.

There's still a difference between types and classes in Python. Try
this in Python:

It raises an error. But the same thing in Io works (which is x :=
Object clone; x number := 5). To do it in Python, you have to do

Also, Python has statements (as opposed to just expressions) even for
the simplest things like writing things to the command line. In Io,
flow control is in functions, which can be overwritten if you want to
change them. The Python philosophy is that if you allow this and other
flexible things to happen, you will have inconsistent code. If someone
asks on the Tutor mailing list how to change that, everybody responds
"Why would you want to do that?". While that's a valid viewpoint, it's
not the most flexible.
Python is 14 years old, and it's still working on global domination;
Io's got some catching up to do :)

I don't think world domination is a practical goal for either
language.

Daniel Ehrenberg
 
D

Daniel Ehrenberg

Sean Ross said:
Hi.
I took a look at Io because of your post, and I have a question:

Are there prototype(?) _and_ instance methods/slots?

In other words, is there a shared state mechanism between instances of a
prototype?
If so, could you show an example.

Thanks,
Sean

I'm not sure how to tell you this so I'll just give a heavily
commented example (comments in Io are with #, //, or /* */)

parentProto := Object clone //makes parentProto a new object
//:= is used for creating new variables
parentProto shared := 5 //parentProto's new slot shared holds 5
parentProto different := 6
parentProto sum := method(shared + different) //self isn't necessary
write("parentProto's sum: ", parentProto sum, "\n")
x := parentProto clone //essentially new instance of parentProto
y := parentProto clone
x different = 2 //just updating slots is done with =.
y different = 3
write("first x sum ", x sum, "\n",
"first y sum ", y sum, "\n")
parentProto shared = 7 //should show up on all clones
write("later x sum", x sum, "\n",
"later y sum", y sum, "\n")

Does that illustrate it well? I know the difference between := and =
is annoying, but you'll get used to it, and it allows really cool
things to be done with scopes and inheretance.

Daniel Ehrenberg
 
S

Sean Ross

[snip example]
Does that illustrate it well? I know the difference between := and =
is annoying, but you'll get used to it, and it allows really cool
things to be done with scopes and inheretance.

I think I get the idea. Let's see:

P = Object clone do( # saw do() used in iolanguage mail archive
allocated = 0
init = method(
id := allocated # semi-colons required?
allocated += 1
)
howMany = method(return allocated)
)

x := P clone
y := P clone
write("allocated? ", P howMany())
# output ...
# allocated? 2

Correct?

Thanks for making me aware of this language, it has some
interesting ideas. I'm not annoyed by the difference between :=
and =. I understand the issue(s) they're trying to solve: you don't
need to declare local variables, or use self, explicitly. I don't have
a problem with that. I imagine it could lead to subtle bugs (if you
update a slot when you meant to set a new one), but that is only
speculation on my part.

I'm not a fan of the parenthesis (which might be expected from
a Python user - love that significant whitespace ;). I'm also not a
big fan of 'clone', I'd rather see 'new', but I do understand why
'clone' is more apt. Of course, in this language, you could always
just say

Object new = Object getSlot("clone")
P = Object new
....

I can appreciate the flexibility of that.
 
J

Josiah Carlson

I'm not sure how to tell you this so I'll just give a heavily
commented example (comments in Io are with #, //, or /* */)

parentProto := Object clone //makes parentProto a new object
//:= is used for creating new variables
parentProto shared := 5 //parentProto's new slot shared holds 5
parentProto different := 6
parentProto sum := method(shared + different) //self isn't necessary
write("parentProto's sum: ", parentProto sum, "\n")
x := parentProto clone //essentially new instance of parentProto
y := parentProto clone
x different = 2 //just updating slots is done with =.
y different = 3
write("first x sum ", x sum, "\n",
"first y sum ", y sum, "\n")
parentProto shared = 7 //should show up on all clones
write("later x sum", x sum, "\n",
"later y sum", y sum, "\n")

Does that illustrate it well? I know the difference between := and =
is annoying, but you'll get used to it, and it allows really cool
things to be done with scopes and inheretance.

Daniel Ehrenberg

After looking at your sample code, I couldn't help but say 'ick'. Maybe
it is my C and Python background, but just putting a space between an
object and its properties looks ugly. The use of method() also makes my
skin crawl.

It is cool that you dig on Io, and I wish you the best.

- Josiah
 
J

Josiah Carlson

IMHO, this makes more sense than iterating over a list created just
for a specific loop. On my computer, Io's loops are faster than
Python's (probably because they are lazy).

Haven't you heard of xrange? It's like range, only without the list
instantiation (and a few other things).

But the community will grow larger in time. The Io community is bigger
than the Python community two years after Python was released
(although that's probably because it wasn't publicly released for two
years).

I would also be willing to bet that is due to the ease of distribution
that the modern internet allows, coupled with the current geek-chic of
learning new-better-than-ever languages. Heck, brainfuck probably has
more users than Python did 2 years after its introduction, but that
doesn't mean that you will be able to write programs that are better,
more efficient, easier to update, easier to debug, etc., with BF than
with some other newer language.

Also, Python has statements (as opposed to just expressions) even for
the simplest things like writing things to the command line. In Io,
flow control is in functions, which can be overwritten if you want to
change them. The Python philosophy is that if you allow this and other
flexible things to happen, you will have inconsistent code. If someone
asks on the Tutor mailing list how to change that, everybody responds
"Why would you want to do that?". While that's a valid viewpoint, it's
not the most flexible.

Perhaps it isn't more flexible. On the other hand, it does allow anyone
to read your code and know that there isn't any magical syntax that is
usable on one Python x.y installation, that isn't on another.

- Josiah
 
J

Jeff Epler

There's still a difference between types and classes in Python. Try
this in Python:


It raises an error. But the same thing in Io works (which is x :=
Object clone; x number := 5). To do it in Python, you have to do

This is because instances of object don't have a __dict__, but instances
of goodObject do. Says the new-style objects document:
Instances of a class that uses __slots__ don't have a __dict__
(unless a base class defines a __dict__); but instances of
derived classes of it do have a __dict__, unless their class
also uses __slots__.

object has slots=[], effectively. The behavior is much like what you'll
see in the example below:
class S(object): __slots__ = ['x']
class T(S): pass

s = S()
s.x = 1 # succeeds
s.y = 2 # fails with exception

t = T()
t.y = 3 # succeeds

Now, whether it's good to have __slots__ and __dict__ I can't tell you,
but it's this wrinkle that caused the behavior you saw, not a
"difference between types and classes".

Jeff
 
P

Paul Prescod

Daniel said:
\>
I'm not sure how to tell you this so I'll just give a heavily
commented example (comments in Io are with #, //, or /* */)

parentProto := Object clone //makes parentProto a new object
//:= is used for creating new variables
parentProto shared := 5 //parentProto's new slot shared holds 5

Do you know why the creator of IO decided not to use the now-ubiquitous
"." operator? I don't know whether I can define a "." operator that has
the appropriate behaviour but that wouldn't help anyhow.

Paul Prescod
 
D

Daniel Ehrenberg

I think I get the idea. Let's see:
P = Object clone do( # saw do() used in iolanguage mail archive
allocated = 0
init = method(
id := allocated # semi-colons required?
allocated += 1
)
howMany = method(return allocated)
)

x := P clone
y := P clone
write("allocated? ", P howMany())
# output ...
# allocated? 2

Correct?
No, init and allocated should have used :=, even though init has
special meaning. I don't really know why you have the howMany method
since it doesn't really do anything (everything is public and methods
can be called with just references), and all clones of P will
dynamically inheret allocated. Also, when initializing variables,
their scope must be specified (which is the whole reason for the
difference between := and =), so id := allocated should probably be
self id := allocated. self, along with a few other variables, is
implicitly sent to all functions (and do).
Thanks for making me aware of this language, it has some
interesting ideas. I'm not annoyed by the difference between :=
and =. I understand the issue(s) they're trying to solve: you don't
need to declare local variables, or use self, explicitly. I don't have
a problem with that. I imagine it could lead to subtle bugs (if you
update a slot when you meant to set a new one), but that is only
speculation on my part.

Actually, the whole scoping model is really that you're working in
transparent objects with inheretance, even for things like the global
scope and the scope of functions.
I'm not a fan of the parenthesis (which might be expected from
a Python user - love that significant whitespace ;).

At first I didn't like them easier, and with the syntax of most
languages, there really isn't any advanatage, but Io code by
convention can be much more dense (while still readible). For example,
Io doesn't have a ternary operator (like Python), but the flow control
function if can be used like one. For example:

x := if(Nil, 1, 0)

would set x to 0. Note that only Nil (and things that emulate Nil) are
false in the context of if.
I'm also not a
big fan of 'clone', I'd rather see 'new', but I do understand why
'clone' is more apt. Of course, in this language, you could always
just say

Object new = Object getSlot("clone")
P = Object new
...

I can appreciate the flexibility of that.

Yeah, that is an advantage. That way of using 'new' will be inhereted
dynamically for all objects.

Daniel Ehrenberg
 
J

John Roth

Paul Prescod said:
Do you know why the creator of IO decided not to use the now-ubiquitous
"." operator? I don't know whether I can define a "." operator that has
the appropriate behaviour but that wouldn't help anyhow.

As far as I know, since everything is a "message send", the dot
is unnecessary. Every operation is either object.paremeter(s),
or syntactic sugar that gets transformed into that format.

John Roth
 
D

Daniel Ehrenberg

Perhaps it isn't more flexible. On the other hand, it does allow anyone
to read your code and know that there isn't any magical syntax that is
usable on one Python x.y installation, that isn't on another.

- Josiah

What are you talking about? There's tons of new "magical syntax" in
Python. Examples for "magical syntax" features that don't work on
1.5.2 include:
*List comprehensions
*String methods
*Generators
*Emulating numeric types
*Nested scopes
* * and ** in function declarations
*for line in this_file
*Subclassing types

There's tons of stuff still being added to Python.

Daniel Ehreberg
 
S

Sean Ross

[snip]
(everything is public and methods
can be called with just references),

Okay. So, is there no way to have private attributes/operations?
Or, like in Python, is there just a convention? "_slotName"

[snip]
Also, when initializing variables,
their scope must be specified (which is the whole reason for the
difference between := and =), so id := allocated should probably be
self id := allocated. self, along with a few other variables, is
implicitly sent to all functions (and do).

Okay. So, if I set 'self id', like so

self id := allocated

and want to update it later in the same block I can use

id = "updated self 'id' slot value"

But, if I later use

id := "set new local 'id' slot value"

in the same block I get a new local variable. Is that how it works?
Or does the first use of a slot determine its assignment scope
for the duration of the block?

self a := "unchanged"
a := "changed"
write("self a is ", self a) # changed or unchanged?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,608
Members
45,251
Latest member
41Ki

Latest Threads

Top