Re: Factor

Factor: the language, the theory, and the practice.

Magic Dict

Tuesday, November 21, 2023

The Raku programming language community comes up with interesting modules, and I enjoy getting emails from the Rakudo Weekly News. This week, there was a link to a post from a few days ago about making a weird data structure in Raku:

I came up with a weird data structure. It’s a hash, but you can also add functions that receive the hash as input so you can do math with it (if you squint, it’s vaguely like a spreadsheet). Something like:

m = MagicDict()
m["a"] = 1
m["b"] = 2
m["sum"] = lambda self: self["a"] + self["b"]

print(m["sum"])

Ideally, I’d want to do this in #RakuLang. (I know it’s possible because I did something much weirder once (I gave Str a CALL-ME method).)

I thought it would be fun to build this in Factor – starting with a magic-dict class that:

  1. wraps an assoc which we will use to store all of the items
  2. has a constructor using a hashtable by default
  3. marked as an instance of assoc, so we support generic words defined on assocs
  4. support the assoc protocol using the delegate vocabulary to defer to the wrapped assoc
TUPLE: magic-dict assoc ;

: <magic-dict> ( -- magic-dict ) H{ } clone magic-dict boa ;

INSTANCE: magic-dict assoc

CONSULT: assoc-protocol magic-dict assoc>> ;

And the main piece of logic we need is to implement the at* lookup word and, if the value is a callable, call it with the assoc on the stack as an argument.

M: magic-dict at*
    swap over assoc>> at* over callable?
    [ drop call( assoc -- value ) t ] [ nipd ] if ;

This allows us to make a Factor version of the original example:

IN: scratchpad <magic-dict>
               1 "a" pick set-at
               2 "b" pick set-at
               [ [ "a" of ] [ "b" of ] bi + ] "sum" pick set-at

IN: scratchpad "sum" of .
3

Pretty cool!