Brian Mitchell
2005-11-08 22:17:50 UTC
Hi everyone,
I've compiled a list over time of things about Io I would like to see
change, features I would like and so on. Bellow are a few of them.
These are really informal and kind of messy but they are better than
nothing. I should put this on the wiki but I am short on time right
now. I will start numbering at 1 so we can refer to them in shorthand
in future times, so please carefully number yours if you do submit any
ICRs.
I have not had time to proof read the following so keep it in mind:
ICR-1 Nil renamed to nil
I propose that Nil should be renamed to nil. This makes it clear that
is should be treated as a value. There has been much talk on IRC about
this already so I won't repeat it.
ICR-2 support richer method names
I propose that we allow ? as a last character in a method name.
I think this leads to more clarity of your intent. isNil would become
nil? for example. Comparing the two it is _very_ clear that nil? is a
question being asked. This would probably lead to more clarity in code
and make conditional statements more readable.
Optional extension to this would also allow ! at the end but the
semantics of this aren't as clear.
ICR-3 set function return self
I propose we unify all set functions (i.e. setFooBar and setQux) to
either return self or to return nil.
This will make behavior more predictable and build some stronger
idioms for Io. self is currently used in most places so it would be
painless to choose that as the return. nil would also work if we
decide that set chaining is a bad practice.
Alternatives would include returning the value set.
ICR-4 IO type hierarchy
I propose we clean up the I/O objects into clearer objects and prototypes.
Currently we are overloading things like File to also control standard
in/out/err. It also makes it hard to check that a type can do I/O but
not specifically what kind. Sockets, Files, Streams, Test Mocks could
all share IO as a basic prototype. This might simplify some of the
interfaces also: File slotNames sort give this:
Io> File slotNames sort
==> asBuffer, asSeq, asString, at, atPut, close, docs, exists, flush,
foreach, groupId, isAtEnd, isDirectory, isLink, isPipe, isRegularFile,
isSocket, isUserExecutable, lastAccessDate, lastDataChangeDate,
lastInfoChangeDate, mode, moveTo, name, open, openForAppending,
openForReading, openForUpdating, path, popen, position,
protectionMode, readBufferOfLength, readLine, readLines,
readStringOfLength, readToBufferLength, remove, rewind, setPath,
setPosition, size, standardError, standardInput, standardOutput, stat,
temporaryFile, truncateToSize, userId, with, write
Notice the large number of isXYZ methods. These could be removed and
made into something like this:
mySocketObject hasProto(Socket)
This will also allow us to add in new I/O features easily in the
future without putting too much burden on File. The hierarchy could be
composed of IO with Buffer and Stream as subs. Then Socket could sub
Stream (no seeking), and File could sub Buffer (seek offered for
example).
ICR-5 do with no arguments
I propose we allow do to take no arguments.
When I code I sometimes throw a bare bones skeleton up. Using stuff like:
Foo := Object clone do(
)
waiting to be filled in. At other times I might want to comment out
something. Commenting out do and having my code templates force me to
type do an unholy number of times has bugged me. Why not change do to
be more friendly and allow zero arguments?
This would be more sugar but I don't see what it is harming.
ICR-6 repeatTimes rename to times
I would like repeatTimes to be renamed to times. I think the current
name is long an isn't elegant for short and clear looping code:
5 repeatTimes( "foo" println )
5 times( "foo" println )
One reads out loud as "Five repeat times, print a line with foo". The
other is similar but more clear: "Five times, print a line with foo".
This is for usability and readability. It not only sounds better but
is much shorter to type. Loops and iterators or things that should
stay light so they don't grow beyond a safe threshold. Small names and
easy translational semantics ensures that the programmer will not be
as easily confused.
ICR-7 repeatTimes with counter
I propose we add an optional count argument to repeatTimes.
When we have a loop it is nice to have a counter in the loop. We
commonly use the for loop to accomplish such things. The for loop is
an ugly artifact (see ICR-8) that make many things unclear.
5 repeatTimes(x, x println)
0
1
2
3
4
This seems to a be a natural behavior.
ICR-8 Remove for loop
I propose we remove the for loop from the language or at least deprecate it.
This one is controversial to some but I will try to lay the case
clearly. The for loop is currently used for different types of
iteration. Two of the types include iteration of a collection and
repetition of a task.
Repetition of a task can easily be handled using constructs like
times, upto, downto, while, until, and so on. These are very powerful
expression but they also are very clear. They each say exactly what
their purpose is while a for loop needs deeper inspection to come to
the same conclusion. There are no for-loop-patterns I know of that are
hard or ugly using alternative mechanisms.
Iteration of collections is another point. I will hit this one very
closely with my other ICRs so I won't give too many examples except to
say that for is a really crummy way to iterate over elements.
In closing I would like to say that for is an archaic structure from
old languages that should be ready to die by now. There is no
requirement that we keep the for loop so why do we? }:-)
ICR-9 foreach renamed to each with some small changes.
I propose that we add internal iterators to all of our collection
objects using each which is very similar to foreach and remove
foreach.
We have a large number of collection types and these are bound to grow
rapidly over time. As we start to use these we will find that we need
to iterate over the elements in the collection with flexible style. If
we added each to our collections we would accomplish a large portion
of what we would need to layer new functionality like mapping and
filtering (or even list comprehension). Right now we have foreach (bad
name) on List which does something _very_ similar though it adds in
the index as a number. I would propose we use the name each and remove
the index unless there are enough arguments to support it when called.
The index would either be an integer count or a key for a key value
pair. Other structures might also make use of this. This is basically
like foreach but a little smarter.
Internal iterators are fine for most activity (I could use some made
up 90% ratio here) but they are sometimes not enough. Sometimes
external iterators are needed. One example is iterating multiple
collections in parallel. In these cases most languages decide to
support external iterators as the basic requirement and build internal
iterators out of them. Io is not like this because we have coroutines
which can handle conversion of an internal iterator to an external
one. Writing an internal iterator is usually easier than writing an
external one so why not make someone else to the work. :) I will cover
this point in another ICR.
ICR-10 Iterator type and iterator returns
I propose that any iterators without parameters instead return a new
Iterator object.
This object would be passable and could be used in the capacity of an
external iterator. It would provide an activation method that would
resume iteration only after it is passed arguments.
twoTimes := 2 times
twoTimes(x, x print)
01
twoTimes("OOH" print)
OOHOOH
someOtherThing := list(42, 24, 42) each
someOtherThing(x, x print)
422442
e := someOtherThing external
while(e hasNext?, e value print; e next)
422442
These are examples of the iterators and how one could convert to an
external iterator. This gives us extreme power for building high order
functions and more customized loops (read again: we don't need for
loops).
ICR-11 hasProto and kindOf
I propose we add kindOf to Object.
Right now hasProto checks not only the ancestors but the current
object. The name is misleading because self is not in the protos list.
Why not have hasProto just check the protos list and kindOf check self
and call hasProto (or check itself if you want). This just makes it a
little more clear of what is included for the check.
ICR-12 upTo and downTo
I propose we add upTo and downTo as basic loop structures.
These are useful things when you need to go over a specific range of integers:
3 upTo(5,x, x print)
345
3 upTo(3, x, x print)
3
3 upTo(2, x, x print)
3 downTo(4, x, x print)
3 downTo(1, x, x print)
321
3 downTo(3, x, x print)
3
These examples give a good idea of how these would work. It is just a
little more expressive than times.
Optional and alternative: define to which would detect direction of
the range. I would like to see all tree (upto, downto, and to)
included.
Optional: The names could be made all low caps because of length.
(also see my Camel to Underscore ICR).
ICR-13 until
I propose we add until to the language as a new loop structure.
As I've said before, clear loops are important. while( cond isNil,
...) is not as nice as until( cond, ....). This is just a step towards
clarity in flow control.
ICR-14 unless
I propose we add unless to the language as a new conditional.
if( cond isNil, ...) is not as clear as unless( cond, ...). I would
like to see this added so we can avoid ugly conditions.
ICR-15 Booleans
I propose we add true and false to the language. false and nil would
be the only values acted on as a "false value" in a boolean
expression.
Right now we have nil overloaded to represent too many things. Like
Lisp this can become ambiguous over time and does not aid in clarity
of intent. Sometimes we also need a non nil return value that
represents something that would be true. An example of this would be
the isNil message. isNil return Lobby which has nothing to do with the
call, it just happens to be something that is treated as true. If find
this messy and poor design. False and true would only help to clear up
comparisons. Seeing Nil returned right now doesn't help as much as
seeing false as you now know more about the source of the value.
Alternative: Only add true. Nil can continue to work as both emptiness
and false.
ICR-16 Expression return values
I propose that all expressions return their lastly evaluated part.
This is already done for the most part. I just want to make it a
standard practice. Things like each and times would return the last
iterations value. This is useful when doing conditionals that rely on
many values and an early break or a positive return will help you
decide on how to manage the condition.
ICR-17 Slot lifetimes and nil
I propose that slots are nil by default and are removed when set to nil.
Right now we have quite a few slot operations: newSlot, removeSlot,
setSlot, getSlot, and updateSlot. We could consolidate and remove the
need to newSlot and removeSlot.
setSlot("a", 1)
setSlot("b", 2)
slotNames sort
a, b
setSlot("a", nil)
slotNames sort
b
ICR-18 Not in booleans
I propose we add not for boolean expressions.
We currently have and and or but no not. This would complete the
symmetry and give us something nicer than isNil (or nil?). Not would
also be appropriate for boolean expressions if we add true and false.
Again the intent is more clear with not.
not foo or bar // very clean short circuit evaluation. familiar to
other languages
ICR-19 Comparable
I propose we add a Comparable proto that would act like a mixin.
Objects that can be compaired with >, <, >=, <=, ==, and between?. If
we add this to collections that support the comparison operator (<=>)
we could accomplish this for generic code. Very simple addition of
sorting and other operations that come for free once we implement <=>.
<=> (a.k.a the spaceship operator), would return zero on equality, 1
when given an argument that is larger than self, and -1 when given an
argument smaller than itself.
l := list(1,2,3,0)
l max print
3
l sort print
0, 1, 2, 3
This mainly serves to aid code reuse and custom user collections and
minimize then number of methods that need to be written each time (one
for many).
ICR-20 Enumerable
I propose we add a Enumerable proto that would act like a mixin.
Enumerable would supply generic methods for things like size, any?,
all?, collect, find, find_all, include?, inject, min, max, sort, zip,
etc... all by providing just the each method on a collection and for a
few, the <=> method.
Again this is for code reuse and rapid building of new collection types.
ICR-21 ===
I propose we add === (a.k.a. the similar to operator).
This operator would be nice when we want to know if something is
similar to another thing. In this case it would be just like hasProto
but with checks on == first.
This is just a convenient operator for case expressions and the like
(see some of my other ICRs). == is a pain to use in these cases if you
want to mach either a value or a type of object.
ICR-22 !=
I propose we add != as a synonym to not(left) ==(right). The compiler
would translate it like it does to := and =.
It is a nice feature that shortens some code. If you don't add unless
and until I would consider these with high priority. It also server a
purpose in custom control structures and iterators.
l := 1 upTo(10) each asList
l filter(x, (x % 2) != 0) print
13579
ICR-23 case expression
I propose we have a case expression that uses the === operator in
selection. It would execute only one block (no fall through) and have
one default case called else.
case(expr,
when 1
...
when 2, 3
...
when IO, FooBarObject
...
when 4, QuxObject, "abc", list(1,2,3)
...
else
...
)
This would make some conditions nicer. It is modeled after ruby if you
need a reference on rules. Note the syntax has open commas. This would
require my ", like cons" ICR.
Alternative syntax:
case(expr
) when(1,
...
) when(2, 3,
...
) when(IO, FooBarObject,
...
) when(4, QuxObject, "abc", list(1,2,3),
...
) else(
...
)
I we don't have this I will probably add it myself anyway. I just
think it is nice enough for the core.
ICR-24 "," cons
I propose we make , and official operator that takes only one argument
with one target (no recursion and in C).
, is like a list cons operator (i.e. like : in Haskell) but for
argument lists more specifically. If we add this to object then we can
construct the arguments list (see ICR-25 and ICR-26 for more info on
the list and my opinions). This would also allow us to do currying or
argument lists :). I think this would be one of the most powerful
features in Io removing the need for really complicated macros to
support nice custom operator syntaxes. I would put this ICR in high
priority.
ICR-25 Argument object type
I propose we make arguments to a function be a special clone of list
that allows for more interactivity.
This would allow us to implement "," on the Argument object to cons
correctly and it would also allow for things like, easy check for
keyword arguments, and other argument introspection methods.
Alternative: Include Arguments as a proto to the instance of a list.
ICR-26 Argument delegation and splat
I propose we have an easier way to delegate all or parts of our argument lists.
When I want to be able to do something like this:
m1 := method(...)
m2 := method(foo, bar, qux, # qux is a list
m1(qux splat) # call with the list splat into place for each argument
)
m2(1,2, list(3,4)) calls m1(3,4) instead of m1(list(3,4)) because of
splat on a list.
ICR-27 thisMessage interface cleanup
I propose some renaming of the thisMessage interface.
Rename thisMessage to this. It is talking about the message or
execution at that point in time so it makes sense (it will confuse C++
guys but who cares, this is not bound by what others do). It will
provide much nice access. Right now meta-programming code is hard to
read because of the long phrases like thisMessage.
this args. a quick way to access arguments. arguments it just too much to type.
That is about it for now. It is just too hard to make a nice var-arg
method right now and heavy looking at that.
ICR-28 better argument handling
I propose that we change how we handle arguments to methods and block,
Right now if I want to have a method that takes a few explicit
arguments and a few optional arguments I have to use the thisMessage
object for all of it. Also, if I want lazy arguments and explicit
declaration I still have to use thisMessage. thisMessage makes sense
but right now it does not support these scenarios very well. I hope to
see this change with the help of a few annotations in a method:
m1 := method(
arg a, b
lazy c
rest d
code(
...
)
)
m1(1,2,foo,4,bar,6)
in the scope of code(..) a = 1, b = 2, c = message(foo), rest =
message(4), message(bar), message(6)
Something to this style where the method gives some locals that are
methods to do the heavy lifting. The result is a method that is
constructed once rather than multiple times, that also has nice
argument semantics. This has some nice potential for optimizations.
I would love to see something like this but better ideas about going
about it would be welcome.
ICR-29 block to function or lambda
I propose we rename block to something less confusing.
I think this would free our capability to discuss code as blocks would
be a generic term for things like methods and lambdas. We could even
leave Block as a generic type that is build off of with method(...)
and lambda(...) as convenience expressions. Io has the capability to
support things like dynamic scoping so leaving the door open would
probably be a good idea. lambda is more traditional but people with
less FP background will not be familiar with it. function or func
could serve as alternative names if lambda is not chosen.
ICR-30 activation
I propose that there be an easy and immediate way of activating
objects, including anonymous blocks.
Right now we can't activate a block without naming it or making a
special constructor (AFAIK). I would like to restore the ability to do
explicit activation on a block:
block(x, x print)(1)
1
I think this ability is useful as the block can actually be used to
get internal information like thisMessage (it is a nice feature for
meta-programming). I don't see how this makes handling the object any
harder. This is not the same as quag's lambda in the wiki PasteBin.
ICR-31 symbols
I propose we have a standard way to address common objects without
causing evaluation (and subsequent activation). Symbols provide this:
b := block( ... )
// Try to pass the block:
foo(getSlot("b")) # messy :(
--- new style ---
b := block( ... )
foo('b)
This would compile into:
b := block( ... )
foo(symbol("b")) # symbol would create the new object
Once the argument is sent to a doMessage then it is evaluated as a
real message. This is very similar to Lisp macros (in fact it is
inspired from them). It gives us the power of lazy arguments in more
than just a method call. I highly recommend this for inclusion.
We could also extend this and allow things like:
getSlot('b) # as a shorhand
ICR-32 macros
I propose we add macro(...) for real support for automated transforms.
This method would make a lot of simple transformations really easy. It
would work like the following:
m := macro(x, x*x) # really simple example
foo := method(x, m(x+1))
There are two main options from this point on: have method
automatically expand macros or leave it to be lazy expansion done only
once on the first invocation. The latter is the easiest and would
involve simple self replacement. This is just a simple way we can have
some nice syntactic shortcuts in our programs. There are probably more
uses. I will probably elaborate why this is really nice to have when I
have more time.
ICR-33 expand
I propose that we add expand() to Io. It would expand macros with an
option argument for the number of levels to expand.
This is a nice congruency with macro(). If we have one we should have the other.
ICR-33 inspect
I propose that objects implements inspect when they want custom
introspective output and that Object have a default inspect method and
that the interactive REPLs use this for object display.
This would free us from asString output that might be more appropriate
for other cases. It also is tailored to a specific use: the developer.
These strings would need to be compact (minimal number of new lines),
and show a large amount of object information. Example of what I came
up with a little while ago:
Object clone do( a := Object clone do( type := "Foo"), b := self, c :=
2, d := list(1,2,3)) inspect
#<Object:0x1107020 a=#<Foo:0x1109130>, b=#<Object:0x1107020 ...>, c=2,
d=[1,2,3]>
This output is short and is "relatively" readable. It could be made
more readable but at the cost of space (some objects would be too
large and more annoying to print). As you notices the type tags would
be used and things like lists and numbers would have their own
implementation of inspect.
ICR-34 Camel vs. Underscore
I propose that we move method names to use underscore syntax and keep
proto objects with CapitalizedCamelCase.
This is another one that will probably live on to be quite
controversial. Very controversial. In fact I really am worried that
this will be dismissed because people are just too used to one style
or another. I will try to make the case clear for why we would be
better off with both.
I grew up on camel case. For years I thought it was the superior way
to go. using _ just seemed to space things out longer then they needed
to be. That changed a few years ago as i started to learn ruby and the
ideas behind their code style. First of all is reading:
HowEasyCanYouReadThis
vs.
how_easy_can_you_read_this
The extra chars in length are a small price to pay for the advance in
that. I don't want to hear arguments against this one as I bet I could
give a double blind experiment that would show which one we can read
easier. The idea is to give separation. The way our minds detects
patterns means it is more likely to pick it up without looking at each
character then the camel cased one. This raises our speed and accuracy
when looking at the code. I will hold an experiment on this if any of
you want to detest the claims made. If you think one is easier than
the other, you should give it some time.
Another point is the fact that we differentiate between Protos and messages:
myMessage
Message
Very little visual for the difference between an name and a Proto
object. The line is fuzzy for what one sees. If we had code like:
MyObject := Object clone do(
each := method(...)
count_external_entries := method(...)
ExternalEntry := Object clone do(...)
)
It is clear what each slot is for. Either a method, a simple value or
an embedded Object that can be used as a prototype. It makes it easy
for other people to see code and go aha because of the large naming
style gap (rather than one letter only).
Another reason: I vote for it ;). j/k. I think we should seriously
consider it for real reasons. Nice code is very important to me. It is
one reason I don't use Perl.
---->8----
Ok. That does it for one day. I have even more (yes. many more) but
these are the ones that came to mind first.
Please have give me any real feedback you have on these. I will post
them to a wiki sometime soon unless someone beats me to it. I can also
elaborate on the topics more if needed.
Thanks for listening,
Brian.
------------------------ Yahoo! Groups Sponsor --------------------~-->
Get Bzzzy! (real tools to help you find a job). Welcome to the Sweet Life.
http://us.click.yahoo.com/A77XvD/vlQLAA/TtwFAA/saFolB/TM
--------------------------------------------------------------------~->
Yahoo! Groups Links
<*> To visit your group on the web, go to:
http://groups.yahoo.com/group/iolanguage/
<*> To unsubscribe from this group, send an email to:
iolanguage-***@yahoogroups.com
<*> Your use of Yahoo! Groups is subject to:
http://docs.yahoo.com/info/terms/
I've compiled a list over time of things about Io I would like to see
change, features I would like and so on. Bellow are a few of them.
These are really informal and kind of messy but they are better than
nothing. I should put this on the wiki but I am short on time right
now. I will start numbering at 1 so we can refer to them in shorthand
in future times, so please carefully number yours if you do submit any
ICRs.
I have not had time to proof read the following so keep it in mind:
ICR-1 Nil renamed to nil
I propose that Nil should be renamed to nil. This makes it clear that
is should be treated as a value. There has been much talk on IRC about
this already so I won't repeat it.
ICR-2 support richer method names
I propose that we allow ? as a last character in a method name.
I think this leads to more clarity of your intent. isNil would become
nil? for example. Comparing the two it is _very_ clear that nil? is a
question being asked. This would probably lead to more clarity in code
and make conditional statements more readable.
Optional extension to this would also allow ! at the end but the
semantics of this aren't as clear.
ICR-3 set function return self
I propose we unify all set functions (i.e. setFooBar and setQux) to
either return self or to return nil.
This will make behavior more predictable and build some stronger
idioms for Io. self is currently used in most places so it would be
painless to choose that as the return. nil would also work if we
decide that set chaining is a bad practice.
Alternatives would include returning the value set.
ICR-4 IO type hierarchy
I propose we clean up the I/O objects into clearer objects and prototypes.
Currently we are overloading things like File to also control standard
in/out/err. It also makes it hard to check that a type can do I/O but
not specifically what kind. Sockets, Files, Streams, Test Mocks could
all share IO as a basic prototype. This might simplify some of the
interfaces also: File slotNames sort give this:
Io> File slotNames sort
==> asBuffer, asSeq, asString, at, atPut, close, docs, exists, flush,
foreach, groupId, isAtEnd, isDirectory, isLink, isPipe, isRegularFile,
isSocket, isUserExecutable, lastAccessDate, lastDataChangeDate,
lastInfoChangeDate, mode, moveTo, name, open, openForAppending,
openForReading, openForUpdating, path, popen, position,
protectionMode, readBufferOfLength, readLine, readLines,
readStringOfLength, readToBufferLength, remove, rewind, setPath,
setPosition, size, standardError, standardInput, standardOutput, stat,
temporaryFile, truncateToSize, userId, with, write
Notice the large number of isXYZ methods. These could be removed and
made into something like this:
mySocketObject hasProto(Socket)
This will also allow us to add in new I/O features easily in the
future without putting too much burden on File. The hierarchy could be
composed of IO with Buffer and Stream as subs. Then Socket could sub
Stream (no seeking), and File could sub Buffer (seek offered for
example).
ICR-5 do with no arguments
I propose we allow do to take no arguments.
When I code I sometimes throw a bare bones skeleton up. Using stuff like:
Foo := Object clone do(
)
waiting to be filled in. At other times I might want to comment out
something. Commenting out do and having my code templates force me to
type do an unholy number of times has bugged me. Why not change do to
be more friendly and allow zero arguments?
This would be more sugar but I don't see what it is harming.
ICR-6 repeatTimes rename to times
I would like repeatTimes to be renamed to times. I think the current
name is long an isn't elegant for short and clear looping code:
5 repeatTimes( "foo" println )
5 times( "foo" println )
One reads out loud as "Five repeat times, print a line with foo". The
other is similar but more clear: "Five times, print a line with foo".
This is for usability and readability. It not only sounds better but
is much shorter to type. Loops and iterators or things that should
stay light so they don't grow beyond a safe threshold. Small names and
easy translational semantics ensures that the programmer will not be
as easily confused.
ICR-7 repeatTimes with counter
I propose we add an optional count argument to repeatTimes.
When we have a loop it is nice to have a counter in the loop. We
commonly use the for loop to accomplish such things. The for loop is
an ugly artifact (see ICR-8) that make many things unclear.
5 repeatTimes(x, x println)
0
1
2
3
4
This seems to a be a natural behavior.
ICR-8 Remove for loop
I propose we remove the for loop from the language or at least deprecate it.
This one is controversial to some but I will try to lay the case
clearly. The for loop is currently used for different types of
iteration. Two of the types include iteration of a collection and
repetition of a task.
Repetition of a task can easily be handled using constructs like
times, upto, downto, while, until, and so on. These are very powerful
expression but they also are very clear. They each say exactly what
their purpose is while a for loop needs deeper inspection to come to
the same conclusion. There are no for-loop-patterns I know of that are
hard or ugly using alternative mechanisms.
Iteration of collections is another point. I will hit this one very
closely with my other ICRs so I won't give too many examples except to
say that for is a really crummy way to iterate over elements.
In closing I would like to say that for is an archaic structure from
old languages that should be ready to die by now. There is no
requirement that we keep the for loop so why do we? }:-)
ICR-9 foreach renamed to each with some small changes.
I propose that we add internal iterators to all of our collection
objects using each which is very similar to foreach and remove
foreach.
We have a large number of collection types and these are bound to grow
rapidly over time. As we start to use these we will find that we need
to iterate over the elements in the collection with flexible style. If
we added each to our collections we would accomplish a large portion
of what we would need to layer new functionality like mapping and
filtering (or even list comprehension). Right now we have foreach (bad
name) on List which does something _very_ similar though it adds in
the index as a number. I would propose we use the name each and remove
the index unless there are enough arguments to support it when called.
The index would either be an integer count or a key for a key value
pair. Other structures might also make use of this. This is basically
like foreach but a little smarter.
Internal iterators are fine for most activity (I could use some made
up 90% ratio here) but they are sometimes not enough. Sometimes
external iterators are needed. One example is iterating multiple
collections in parallel. In these cases most languages decide to
support external iterators as the basic requirement and build internal
iterators out of them. Io is not like this because we have coroutines
which can handle conversion of an internal iterator to an external
one. Writing an internal iterator is usually easier than writing an
external one so why not make someone else to the work. :) I will cover
this point in another ICR.
ICR-10 Iterator type and iterator returns
I propose that any iterators without parameters instead return a new
Iterator object.
This object would be passable and could be used in the capacity of an
external iterator. It would provide an activation method that would
resume iteration only after it is passed arguments.
twoTimes := 2 times
twoTimes(x, x print)
01
twoTimes("OOH" print)
OOHOOH
someOtherThing := list(42, 24, 42) each
someOtherThing(x, x print)
422442
e := someOtherThing external
while(e hasNext?, e value print; e next)
422442
These are examples of the iterators and how one could convert to an
external iterator. This gives us extreme power for building high order
functions and more customized loops (read again: we don't need for
loops).
ICR-11 hasProto and kindOf
I propose we add kindOf to Object.
Right now hasProto checks not only the ancestors but the current
object. The name is misleading because self is not in the protos list.
Why not have hasProto just check the protos list and kindOf check self
and call hasProto (or check itself if you want). This just makes it a
little more clear of what is included for the check.
ICR-12 upTo and downTo
I propose we add upTo and downTo as basic loop structures.
These are useful things when you need to go over a specific range of integers:
3 upTo(5,x, x print)
345
3 upTo(3, x, x print)
3
3 upTo(2, x, x print)
3 downTo(4, x, x print)
3 downTo(1, x, x print)
321
3 downTo(3, x, x print)
3
These examples give a good idea of how these would work. It is just a
little more expressive than times.
Optional and alternative: define to which would detect direction of
the range. I would like to see all tree (upto, downto, and to)
included.
Optional: The names could be made all low caps because of length.
(also see my Camel to Underscore ICR).
ICR-13 until
I propose we add until to the language as a new loop structure.
As I've said before, clear loops are important. while( cond isNil,
...) is not as nice as until( cond, ....). This is just a step towards
clarity in flow control.
ICR-14 unless
I propose we add unless to the language as a new conditional.
if( cond isNil, ...) is not as clear as unless( cond, ...). I would
like to see this added so we can avoid ugly conditions.
ICR-15 Booleans
I propose we add true and false to the language. false and nil would
be the only values acted on as a "false value" in a boolean
expression.
Right now we have nil overloaded to represent too many things. Like
Lisp this can become ambiguous over time and does not aid in clarity
of intent. Sometimes we also need a non nil return value that
represents something that would be true. An example of this would be
the isNil message. isNil return Lobby which has nothing to do with the
call, it just happens to be something that is treated as true. If find
this messy and poor design. False and true would only help to clear up
comparisons. Seeing Nil returned right now doesn't help as much as
seeing false as you now know more about the source of the value.
Alternative: Only add true. Nil can continue to work as both emptiness
and false.
ICR-16 Expression return values
I propose that all expressions return their lastly evaluated part.
This is already done for the most part. I just want to make it a
standard practice. Things like each and times would return the last
iterations value. This is useful when doing conditionals that rely on
many values and an early break or a positive return will help you
decide on how to manage the condition.
ICR-17 Slot lifetimes and nil
I propose that slots are nil by default and are removed when set to nil.
Right now we have quite a few slot operations: newSlot, removeSlot,
setSlot, getSlot, and updateSlot. We could consolidate and remove the
need to newSlot and removeSlot.
setSlot("a", 1)
setSlot("b", 2)
slotNames sort
a, b
setSlot("a", nil)
slotNames sort
b
ICR-18 Not in booleans
I propose we add not for boolean expressions.
We currently have and and or but no not. This would complete the
symmetry and give us something nicer than isNil (or nil?). Not would
also be appropriate for boolean expressions if we add true and false.
Again the intent is more clear with not.
not foo or bar // very clean short circuit evaluation. familiar to
other languages
ICR-19 Comparable
I propose we add a Comparable proto that would act like a mixin.
Objects that can be compaired with >, <, >=, <=, ==, and between?. If
we add this to collections that support the comparison operator (<=>)
we could accomplish this for generic code. Very simple addition of
sorting and other operations that come for free once we implement <=>.
<=> (a.k.a the spaceship operator), would return zero on equality, 1
when given an argument that is larger than self, and -1 when given an
argument smaller than itself.
l := list(1,2,3,0)
l max print
3
l sort print
0, 1, 2, 3
This mainly serves to aid code reuse and custom user collections and
minimize then number of methods that need to be written each time (one
for many).
ICR-20 Enumerable
I propose we add a Enumerable proto that would act like a mixin.
Enumerable would supply generic methods for things like size, any?,
all?, collect, find, find_all, include?, inject, min, max, sort, zip,
etc... all by providing just the each method on a collection and for a
few, the <=> method.
Again this is for code reuse and rapid building of new collection types.
ICR-21 ===
I propose we add === (a.k.a. the similar to operator).
This operator would be nice when we want to know if something is
similar to another thing. In this case it would be just like hasProto
but with checks on == first.
This is just a convenient operator for case expressions and the like
(see some of my other ICRs). == is a pain to use in these cases if you
want to mach either a value or a type of object.
ICR-22 !=
I propose we add != as a synonym to not(left) ==(right). The compiler
would translate it like it does to := and =.
It is a nice feature that shortens some code. If you don't add unless
and until I would consider these with high priority. It also server a
purpose in custom control structures and iterators.
l := 1 upTo(10) each asList
l filter(x, (x % 2) != 0) print
13579
ICR-23 case expression
I propose we have a case expression that uses the === operator in
selection. It would execute only one block (no fall through) and have
one default case called else.
case(expr,
when 1
...
when 2, 3
...
when IO, FooBarObject
...
when 4, QuxObject, "abc", list(1,2,3)
...
else
...
)
This would make some conditions nicer. It is modeled after ruby if you
need a reference on rules. Note the syntax has open commas. This would
require my ", like cons" ICR.
Alternative syntax:
case(expr
) when(1,
...
) when(2, 3,
...
) when(IO, FooBarObject,
...
) when(4, QuxObject, "abc", list(1,2,3),
...
) else(
...
)
I we don't have this I will probably add it myself anyway. I just
think it is nice enough for the core.
ICR-24 "," cons
I propose we make , and official operator that takes only one argument
with one target (no recursion and in C).
, is like a list cons operator (i.e. like : in Haskell) but for
argument lists more specifically. If we add this to object then we can
construct the arguments list (see ICR-25 and ICR-26 for more info on
the list and my opinions). This would also allow us to do currying or
argument lists :). I think this would be one of the most powerful
features in Io removing the need for really complicated macros to
support nice custom operator syntaxes. I would put this ICR in high
priority.
ICR-25 Argument object type
I propose we make arguments to a function be a special clone of list
that allows for more interactivity.
This would allow us to implement "," on the Argument object to cons
correctly and it would also allow for things like, easy check for
keyword arguments, and other argument introspection methods.
Alternative: Include Arguments as a proto to the instance of a list.
ICR-26 Argument delegation and splat
I propose we have an easier way to delegate all or parts of our argument lists.
When I want to be able to do something like this:
m1 := method(...)
m2 := method(foo, bar, qux, # qux is a list
m1(qux splat) # call with the list splat into place for each argument
)
m2(1,2, list(3,4)) calls m1(3,4) instead of m1(list(3,4)) because of
splat on a list.
ICR-27 thisMessage interface cleanup
I propose some renaming of the thisMessage interface.
Rename thisMessage to this. It is talking about the message or
execution at that point in time so it makes sense (it will confuse C++
guys but who cares, this is not bound by what others do). It will
provide much nice access. Right now meta-programming code is hard to
read because of the long phrases like thisMessage.
this args. a quick way to access arguments. arguments it just too much to type.
That is about it for now. It is just too hard to make a nice var-arg
method right now and heavy looking at that.
ICR-28 better argument handling
I propose that we change how we handle arguments to methods and block,
Right now if I want to have a method that takes a few explicit
arguments and a few optional arguments I have to use the thisMessage
object for all of it. Also, if I want lazy arguments and explicit
declaration I still have to use thisMessage. thisMessage makes sense
but right now it does not support these scenarios very well. I hope to
see this change with the help of a few annotations in a method:
m1 := method(
arg a, b
lazy c
rest d
code(
...
)
)
m1(1,2,foo,4,bar,6)
in the scope of code(..) a = 1, b = 2, c = message(foo), rest =
message(4), message(bar), message(6)
Something to this style where the method gives some locals that are
methods to do the heavy lifting. The result is a method that is
constructed once rather than multiple times, that also has nice
argument semantics. This has some nice potential for optimizations.
I would love to see something like this but better ideas about going
about it would be welcome.
ICR-29 block to function or lambda
I propose we rename block to something less confusing.
I think this would free our capability to discuss code as blocks would
be a generic term for things like methods and lambdas. We could even
leave Block as a generic type that is build off of with method(...)
and lambda(...) as convenience expressions. Io has the capability to
support things like dynamic scoping so leaving the door open would
probably be a good idea. lambda is more traditional but people with
less FP background will not be familiar with it. function or func
could serve as alternative names if lambda is not chosen.
ICR-30 activation
I propose that there be an easy and immediate way of activating
objects, including anonymous blocks.
Right now we can't activate a block without naming it or making a
special constructor (AFAIK). I would like to restore the ability to do
explicit activation on a block:
block(x, x print)(1)
1
I think this ability is useful as the block can actually be used to
get internal information like thisMessage (it is a nice feature for
meta-programming). I don't see how this makes handling the object any
harder. This is not the same as quag's lambda in the wiki PasteBin.
ICR-31 symbols
I propose we have a standard way to address common objects without
causing evaluation (and subsequent activation). Symbols provide this:
b := block( ... )
// Try to pass the block:
foo(getSlot("b")) # messy :(
--- new style ---
b := block( ... )
foo('b)
This would compile into:
b := block( ... )
foo(symbol("b")) # symbol would create the new object
Once the argument is sent to a doMessage then it is evaluated as a
real message. This is very similar to Lisp macros (in fact it is
inspired from them). It gives us the power of lazy arguments in more
than just a method call. I highly recommend this for inclusion.
We could also extend this and allow things like:
getSlot('b) # as a shorhand
ICR-32 macros
I propose we add macro(...) for real support for automated transforms.
This method would make a lot of simple transformations really easy. It
would work like the following:
m := macro(x, x*x) # really simple example
foo := method(x, m(x+1))
There are two main options from this point on: have method
automatically expand macros or leave it to be lazy expansion done only
once on the first invocation. The latter is the easiest and would
involve simple self replacement. This is just a simple way we can have
some nice syntactic shortcuts in our programs. There are probably more
uses. I will probably elaborate why this is really nice to have when I
have more time.
ICR-33 expand
I propose that we add expand() to Io. It would expand macros with an
option argument for the number of levels to expand.
This is a nice congruency with macro(). If we have one we should have the other.
ICR-33 inspect
I propose that objects implements inspect when they want custom
introspective output and that Object have a default inspect method and
that the interactive REPLs use this for object display.
This would free us from asString output that might be more appropriate
for other cases. It also is tailored to a specific use: the developer.
These strings would need to be compact (minimal number of new lines),
and show a large amount of object information. Example of what I came
up with a little while ago:
Object clone do( a := Object clone do( type := "Foo"), b := self, c :=
2, d := list(1,2,3)) inspect
#<Object:0x1107020 a=#<Foo:0x1109130>, b=#<Object:0x1107020 ...>, c=2,
d=[1,2,3]>
This output is short and is "relatively" readable. It could be made
more readable but at the cost of space (some objects would be too
large and more annoying to print). As you notices the type tags would
be used and things like lists and numbers would have their own
implementation of inspect.
ICR-34 Camel vs. Underscore
I propose that we move method names to use underscore syntax and keep
proto objects with CapitalizedCamelCase.
This is another one that will probably live on to be quite
controversial. Very controversial. In fact I really am worried that
this will be dismissed because people are just too used to one style
or another. I will try to make the case clear for why we would be
better off with both.
I grew up on camel case. For years I thought it was the superior way
to go. using _ just seemed to space things out longer then they needed
to be. That changed a few years ago as i started to learn ruby and the
ideas behind their code style. First of all is reading:
HowEasyCanYouReadThis
vs.
how_easy_can_you_read_this
The extra chars in length are a small price to pay for the advance in
that. I don't want to hear arguments against this one as I bet I could
give a double blind experiment that would show which one we can read
easier. The idea is to give separation. The way our minds detects
patterns means it is more likely to pick it up without looking at each
character then the camel cased one. This raises our speed and accuracy
when looking at the code. I will hold an experiment on this if any of
you want to detest the claims made. If you think one is easier than
the other, you should give it some time.
Another point is the fact that we differentiate between Protos and messages:
myMessage
Message
Very little visual for the difference between an name and a Proto
object. The line is fuzzy for what one sees. If we had code like:
MyObject := Object clone do(
each := method(...)
count_external_entries := method(...)
ExternalEntry := Object clone do(...)
)
It is clear what each slot is for. Either a method, a simple value or
an embedded Object that can be used as a prototype. It makes it easy
for other people to see code and go aha because of the large naming
style gap (rather than one letter only).
Another reason: I vote for it ;). j/k. I think we should seriously
consider it for real reasons. Nice code is very important to me. It is
one reason I don't use Perl.
---->8----
Ok. That does it for one day. I have even more (yes. many more) but
these are the ones that came to mind first.
Please have give me any real feedback you have on these. I will post
them to a wiki sometime soon unless someone beats me to it. I can also
elaborate on the topics more if needed.
Thanks for listening,
Brian.
------------------------ Yahoo! Groups Sponsor --------------------~-->
Get Bzzzy! (real tools to help you find a job). Welcome to the Sweet Life.
http://us.click.yahoo.com/A77XvD/vlQLAA/TtwFAA/saFolB/TM
--------------------------------------------------------------------~->
Yahoo! Groups Links
<*> To visit your group on the web, go to:
http://groups.yahoo.com/group/iolanguage/
<*> To unsubscribe from this group, send an email to:
iolanguage-***@yahoogroups.com
<*> Your use of Yahoo! Groups is subject to:
http://docs.yahoo.com/info/terms/