Snap! 8 metaprogramming use cases

If this is the wrong category, please shift it.

Hi (and apologies to bh and Jens for not posting here at all or really using the cool community site in their entire existence :slight_smile: ). Congrats on Snap! 8. That's a big version number, and the new metaprogramming features that have finally arrived are too cool for me to ignore (though usually I can never think of anything worth programming).

I haven't found much discussion on them here yet (adamantpenguin implemented text-based Snap!) so here are some use case ideas, and please share any more...

Records: I don't know where I first heard of this kind of thing; probably because of the SRFIs (Scheme Requests for Implementation) about it.

records script pic (4)

It will be great if someone can think of a better example for this.

The way to use variadic inputs seems to be undocumented, so I assumed there was no way and made it nest a lot of CONSes instead of using LIST.

There doesn't seem to be a better way to deal with variables than this:

or using an undefined variable if it has a fixed name, but of course I should just have made a custom block for this purpose.

Backquote (can't think of a different name): This macro might make a lot of macros (and the record implementation) easier and clearer. Not sure if it works perfectly, or whether unevaluated inputs/C-slots or ordinary procedure inputs are better. THIS SCRIPT + OF (if the latter now means "this block/procedure as a procedure with the environment of another") doesn't seem to work very consistently:

works, but this should be equivalent and doesn't work:

(Also I seem to have broken something so the backquote block only works half the time, other times saying "Cannot convert a Symbol value to a string" or something.)

A library with blocks for metaprogramming/macros might be a good idea.

bh had thought of a metacircular evaluator well before these features weere added. I guess running primitives needn't feel like "cheating" because special forms would still be treated specially? Are there any interesting ways it could be modified? Would the "Variations on a Scheme" still be relevant even though Snap! can imitate them more directly? In any case, it'd be interesting. (I started one with Snap! XML long ago.)

I don't think pattern matching (or syntax-rules) would be really relevant to Snap! macros because they're blocks.

Dispatch procedure OOP: Now one could make a DEFINE CLASS block that puts a new constructor in the palette, rather than just letting one assign it to a variable.

Snap! is probably one of the most innovative programming languages today (OK, I know nothing about programming languages today or CS academia, but I have the suspicion) since you're willing to add natural, convenient features without feeling constrained to what's conventional (hyperblocks, representing procedures' names(?) as themselves for want of a better way to put it, and hopefully eventually hybrid lexical/dynamic scope).

Any thoughts/ideas?

Thank you!

Yes, records are Jens's first use case. I imagine there will be a library reasonably soon. I agree with your notation for selectors, but Jens wants one selector block with a menu input.

Oh you're right! I didn't put the variadic input thing in the manual. Will fix that next edition. When JOINing a split expression, the actual input value sublist for a variadic input can either exactly match the number of slots shown in the block at the head of the sublist, e.g., if the block is untitled script pic (3) then you provide four input expressions, or else you head the input expression list with a small integer and then a list of that many input expressions. I think I have that right...

Backquote: I have a partial implementation here:

but it doesn't quite work because of the issue about variadic inputs discussed in the previous paragraph. Gotta get back to it...

Yes, a metacircular evaluator is definitely something we should be able to do if we're going to call ourselves a Scheme! I don't think we're quite there yet, or rather, I don't think we have the library spec'd out to let it be anything like as elegant as the SICP one.

Oh, yeah, how does blockness get in the way of syntax-rules?

Yes, DEFINE CLASS should add to the palette. Maybe it'd be a wrapper around a CLASS block that reports the constructor, and just wraps a name around that result.

Jens has convinced me that hybrid scope would horribly slow down even programs that don't use it, because to be sure you don't use it you have to unwind the stack frame by frame, rather than just pop off a bunch of stuff. Instead I might have to put up with a FLUID-LET that doesn't require you defining a global variable first. (It would create the variable the first time you fluid-let it.) Maybe with some syntax around it so that you can have a DYNAMIC VARS block similar to SCRIPT VARS but marking the variable as dynamic, or something.

We don't support declarative programming yet. I'm hoping, though, that it can be implemented entirely in Snap!, rather than having to modify Snap!. It'll depend how slow it is, I guess. Another thing for my task list. For innovative language, take a look at Oz, which implements OOP and functional programming and declarative programming, not as separate pieces awkwardly glued together, but by using an associative store as the main data structure, in terms of which all those things are defined.

Unfortunately, my task list has been write-only for a while now. :~( I really have to get myself to get back to work!

Oh, cool. That's much better. Like I tried to explain it doesn't work if you need a parameter of a reporter ring either, but then again it is called THIS SCRIPT so that shouldn't come as a surprise (and it won't usually matter).

This is what I meant:

but of course that wouldn't work with custom blocks (however you'd choose to do custom blocks). So instead either the environment could bind each block to itself so it can be directly looked up, or one could use LABEL OF ... (which I forgot about) and keep them separate from variables.

I didn't mean it gets in the way so much as that you don't need it, because in Scheme it'd be used for different variants of a special form with the same keyword, or for sublists or words like ELSE. But on second thought it could be useful for something with variadic inputs and subblocks (like the multi-branched conditional library), since whereas before one could only have made a pattern matcher for lists, now one can make one for arbitrary blocks. I just haven't thought of anything that could be done that way that couldn't be done before.

Right, I basically meant before one could make CLASS but now one can make DEFINE CLASS (and now I'll stop worrying about before and after :joy:).

Wouldn't that only occur when there's an undefined variable (if the lexical environment is searched first)?

Do you mean logic programming? I ported the logic programming system from SICP in 2016. I figure you mean something more visual and less list-heavy? (For one thing your idea regarding IN FRONT OF would work now.) Now each predicate could be a command block, if that's sort of your idea?

Thanks. I looked at some examples and it's a bit too smart for me right now :joy:

Why not? CALL works for custom blocks.

I'm also not convinced that you don't have to evaluate the operator. It could be a call to a block that reports a block, for example. It could be a variable whose value is a block. It could be an if/else that chooses one of two blocks to run.

If you want a complete implementation, then EVAL has to know about hyperblocks. And we have to flag inputs that are hyperizable.

The canonical case is to implement COND as a macro that reports an expression with nested IFs. But there are bunches of Scheme forms that are specified in the manual as implementable in Scheme. See section 4.2, Derived expression types, page 10 of r5rs.

That's what I thought, but Jens convinced me that it takes time to unwind the stack when a procedure returns, because of shallow binding. (And if you don't use shallow binding, then every reference to a global variable has to traverse the stack to make sure it actually is the global you wanted, rather than a local variable of the same name.)

Oh, right, so you did. So maybe I just have to make that a library. I'll go through it again. That'll save me some work! :~)

If you want the metacircular evaluator to have its own set of custom blocks, or at least want the metacircular evaluator to evaluate their definitions, then you wouldn't simply call the operator.

Then the operator (the actual block of which there's an instance) would be CALL or RUN (Snap! is a Lisp-2, right?). But I concluded that it wouldn't just directly call the operator, unless it's programmed with only rings instead of custom blocks.

Yes, I was just thinking of when SYNTAX-RULES is more convenient than macros without pattern matching. Most of those derived special forms have sublists like COND cases or LET bindings (which can be blocks in Snap!). If there's more than one SYNTAX-RULES case, then it's probably either recursive, or a different variant like named LET (which would just be another block in Snap!). (Or something more complex.)

Alright. I guess I was thinking that global variables would be treated like any other (lexical) variable, and then (since the lexical environment is searched first) there wouldn't be such a problem if shallow binding isn't used, but then of course one wouldn't be able to override global variables (the nearest would be a script variable in one of the main scripts). It might still be handy for subprocedures or whatever.

Good point. So you have to implement APPLY, which calls EVAL-SEQUENCE on the body, with an environment that... but you know all this.

Wait, I didn't look thoroughly enough. It's not explicitly mentioned, but BLOCKIFY demonstrates it clearly. My bad. :P

And I found a project I made for generic procedures based on predicates, which I adapted now so they can be palette blocks. (Probably not what you need if you want a hierarchy of types, but it could be changed to use type tags or something. Or somebody could implement CLOS in Snap!. That would be really cool.)

Oh geez. That would be the opposite of Schemeliness! Objects are just closures, as we say in Schemeland. :~)

What about this then? :P No, you have a point. (And I still haven't read SICP from cover to cover or done the exercises...) I more thought the idea interesting than had any use for it. The main advantage, other than that it uses ordinary-looking procedure calls, is that it can dispatch on any of the arguments' types. Although explicit message passing is probably more object-oriented.

I filed two issues on the things I mentioned here (neither are big problems).

Wow, that's much simpler than my implementation of define-class. It's because I had a lot of syntax, so inside a class definition (implemented by a hairy macro) you could have clauses such as

(instance-vars (foo bar garply))
(method (plus other)
        (+ self other))

My answer about multiple parents, by the way, is that a method in your first grandparent comes before a method in your second parent. And if generic-apply can't find a method that exactly fits all the actual arguments (after considering methods in parent objects, of course) then it should give an error message.

(Just between us, I've never found much application for OOP in real life, although the Snap! implementation uses objects heavily.)

Finish SICP! What can you possibly have to do that's more important? :~P