After a period of slower progress I got reignited regarding Spry. So far I have written a lot of articles about Spry, and during this time things have evolved and changed.
So I am now trying to “clean house” on where Spry stands today. This sweep through the old articles is a first step, then I will update the language manual to be 100% in sync with the implementation.
Let’s go through the articles from the beginning!
Things wrong in article one:
funci
is now calledmethod
, indicating it is a message to a receiver on the leftif
as been changed to the more Smalltalkishthen:else:
(but shorter thanifTrue:ifFalse:
and reads nice)return
is now^
just like in Smalltalk
In article two:
- Context is now called Map. It’s the same as a Smalltalk Dictionary, but “Map” is a more popular term these days, and shorter.
- Get words use
$
-prefix, not^
(which is return). This actually looks kinda ok, it makes them resemble “variables” from other languages. - The idea with a pluggable parser… I think I will skip that and replace with something else in pure Spry!
- The scoping prefixes
.
and..
have been removed and replaced with another way to assign. - I wrote “This means Ni maintains no call stack of its own.” - no idea what I meant by that. Spry indeed has a stack of activation records.
In article three:
- There is no
ifelse
, instead we havethen:else:
,else:then:
,else:
,then:
. - The argwords design mistake is still here, but… can be avoided by not using argwords nested, so use
[:x :y ^[x + y]]
instead of[^[:x + :y]]
. So it will probably have to stay. - Single
?
is not used anymore, we can useset?
to check if an unevaluated word is bound, likex set?
- Meaning of scopes are now (I have removed
.
and..
):x
- look in locals and then all the way out.@x
- look in closest surrounding Map (self).
- Optional arguments have not been added, I don’t think I ever will add.
&
has been changed to Smalltalk style,
for concatenation, but leaning towards+
.undef
vsnil
is slightly different:nil
is a value that means “no value”undef
has been removed, it was a fun experiment
?
-marks are used for funcs/methods that return booleans, convention.!
-mark is still unused.self
is as the receiver for methods. It can be anything.context
islocals
, returns the Map of the local scope.activation
returns the current activation, not yet explored much but it’s there!
In article four:
- I later opted to call a Dictionary a Map, so it’s Map now :)
- The
bindings
word is nowlocals
. - The description of
object
is partly the way it works, yes, it takes a Map as argument and returns an “object”. But… this is done using tags that’s not described in this article. - Also, the traits idea and thoughts around that is not what has been implemented later. The current Spry instead is exploring if methods can be defined for things with specific tags, and then if those methods can be automatically composed into “polymethods” so that two different modules can define methods with the same name - and they are “merged” into a polymethod that get’s bound to that name. When invoked the polymethod should “internally” pick which method to execute.
In article five:
- Yes, renamed to Spry. But… it’s sprylang.se, not .org
In article six:
- Yes, base idea of using compressed “Spry code” and store in a fast embedded database still stands! But I now have Rocksdb instead of Sophia, and a pure Nim implementation of Snappy instead of lz4 (it was too hard to get on various platforms)
- Yes, the current modules is implemented as described. Not tested much, but yes. I still think the base ideas stand.
- I did fix so that
Foo::bar
first finds Foo, and then looks in it. So it works for Maps in general, not just modules. And yes, you can then shadow a module with a global. - To access “map members” I instead added
@x
syntax. Sinceself
is now bound to the receiver of methods, this resolves members also. One issue though, if you use blocks (and not funcs) then nested blocks in a method sent as parameters to other methods will obviously not resolve to the lexical self, but to the receiving self. Changing the block to a func solves this. - Lossless AST was something I did experiment with, but dropped it. A bit too crazy perhaps.
- So… otherwise it basically sounds like a good plan, but not used much (or implemented much) yet.
In article nine
- This article is mostly “fluff” but there is one important thing I need to improve - “Live within the prototype”. In other words, Spry code should be edited, browsed and debugged in Spry tools. I need to try to get that going!
In article ten
- Nothing to note, except that yeah, Nim is fast and Spry is slow :)
- Most of this article seems quite correct and according to plan.
And finally, in article twelve
- Most here seems fine too.
Latest Changes
- I have added
:=
for reassignment. If no existing binding is found there will be an error (not fixed yet). So=
will work fine for single assignment, and will assign in local scope. And:=
signify reassignment and will lookup outwards before assigning. - I have removed
undef
. It felt neat but get’s confusing. Maps can still hold nil, it’s a valid value - if you want to check for a missing binding you will have to use explicit calls to do it instead, just as in Smalltalk. - I have removed
.
and..
scoping words. They can instead be implemented as direct access tolocals
oractivation parent lookup:
etc. - I have implemented a base
catch:
,throw
andtry:catch:
mechanism but more to come.
More on error handling in the next article!