I keep reading how goal-driven execution model looks like, and while I really like natural feel of such condition:
if (a < b < c)
yesterday I found the piece of code which put me off. Consider printing out the just-read line in Icon:
write(read())
When read
hits the EOF
it fails, and thus entire chain of expressions fails — here it means write
won’t be called. Let’s say you would like to catch the content of the line nevertheless, so you do a little rewrite:
line := read()
write(line)
OK, we have 2 lines, but the code basically does the same thing… Wait, it does not. It is not even close. Now when read
hits the EOF
the chain of expressions ends at the assignment, so it will be omitted and line
will keep its previous value. Since the second line is completely unrelated it will be executed with the old line
value.
Sure, I am surprised, and maybe part of the surprise is because I am not used to the way Icon works. But I think transparency of adding temporary objects to keep intermediate results is pretty fundamental concept, and Icon breaks it — something I cannot like.
From that point there are two approaches — give up or design improved goal-driven model (it might the same path though).
As for giving up I went back to my old idea of returning tuple — pair of function outcome and error:
if ((success:@,value) = dict["hello"]).success then
// we have direct access to value
// success is dropped
The syntax has a lot to be desired, so how about adding when
construct which could handle exactly such cases:
when value = dict["hello"] then
// we have direct access to value
This would be syntactic sugar only — pure internal rewrite to the if
presented above.
And then it struck me — forget about Lisp homoiconicity, uniformity stupid! Lispers have to struggle with ugly syntax:
(if (= 4 (+ 2 2)) (...
instead of clean “syntax for the masses”:
if 4 = 2+2 then...
but Lisp syntax is uniform, and because of that Lispers don’t have to wait for anyone to bring new construct to the language. They add their when
whenever they like — it blends in perfectly.
On the other hand in C-derivatives all you have is function-like expansion, you cannot add another for
or while
. Any attempt to bring Lisp-macro to C world would require allowing the user to alter the grammar of the language.
I didn’t solve anything with goal-driven model — instead I added yet another puzzle for myself. What to sacrifice in the design to bring power of macros to Skila?
If you find this topic interesting, more readings for you — The Nature of Lisp, Homoiconicity isn’t the point and Lisp: It’s Not About Macros, It’s About Read.