Skip to content

Instantly share code, notes, and snippets.

@gokr
Created May 29, 2020 22:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gokr/43ebe7decdce26c72589bf3032885f70 to your computer and use it in GitHub Desktop.
Save gokr/43ebe7decdce26c72589bf3032885f70 to your computer and use it in GitHub Desktop.
A sweep over the old Spry articles

Spry moves again

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 write down a summary on where Spry stands today, the changes I would like to make, and what I am doing next.

Let's go through the articles from the beginning, things wrong in article one:

  • funci is now called method
  • if as been changed to the more Smalltalkish then:else:
  • return is now ^ just like in Smalltalk

In article two:

  • Context is now called Map. It's the same as a Smalltalk Dictionary.
  • Get words use $-prefix, not ^ (which is return)
  • 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.
  • 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 have then: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 use set? to check if an unevaluated word is bound, like x set?
  • Meaning of scopes are now (but I am planning to cut out . and ..):
    • x - look in locals and then all the way out.
    • @x - look in closest surrounding Map (self).
  • Optional arguments have not been added.
  • & has been changed to Smalltalk style , for concatenation, but leaning towards +.
  • undef vs nil is slightly different:
    • nil is a value that means "no value".
    • undef has been removed
  • ?-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 is locals, 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 now locals.
  • 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)

In article seven

  • Yes, the current modules is implemented as described. Not tested much, but yes. I still think the base ideas stand.

In article eight

  • 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. Since self 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 :)

In article eleven

  • Most of this article seems quite correct and according to plan.

And finally, in article twelve

  • Most here seems fine too.

Changes planned

The following describes changes I wish to do to the language right now:

  • I have added := for reassignment. If no existing binding is found there is an error.
  • 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 similarly removed . and .. scoping words. They can instead be implemented as direct access to locals or activation parent lookup: etc.
  • I would like to change concatenation to +, even Dart does it these days. It also frees , for something else, see below.
  • I will remove pluggable literals and later do something else.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment