I want to write a Define Type block that will use the JS facility. Its inputs will be a type name and a list of field names for that type. When run, it will create a new category and several new blocks in that category. For example, Define Type [Person] [name] [age] will generate a make_Person [name] [age] block that will create and report a 3-element list ["Person" name age], accessor functions, etc.
Any pointers to entry points in the JS code base would be much appreciated! Do I start with one of the BlockMorph families?
Oh gosh, Desmond, I'm really behind answering my emails and getting back to you, sorry!
Can I encourage you to just hold back a little on what you're trying to do? I'm currently working on exactly a mechanism that will let you do all of that without using JS (i.e. macros). But I'm also somewhat worried about your pedagogical approach and would love to understand more. Man, I know it's a tough time to try to get in touch with me....
No worries, Jens. I would use macros if they were here. There's a bit more context in my email to interest @ microblocks (reaches you, hopefully) that speaks to timeline, and why I want to do a temporary JS version before macros land.
Pedagogical: Hand-crafted constructor, accessors, etc. for a concrete data type when introducing ADTs is great. SICP style. Using pure functions as the core representation machinery is great, when teaching layered flexibility down to the metal. Also SICP style.
At other times students can focus on far more straightforward definition of data types, often just record and union, not themselves based on or containing functions. A Shape is either a Circle or a Rect or a Line, or an Image. Just commit to a concrete data representation for each. A draw function that takes a Shape and handles the variants is an adequate layer of abstraction. The Tagged Data section p. 237 in SICP.
In this context they don't need to hand-craft that supporting constructor-accessor-predicate machinery each time. They also don't need to retain ultimate flexibility by using functions down to the metal (like SICP painters p.185). In this approach data design = function design, in importance, but not in form.
It is such a relief discussing curriculum issues with someone who understands SICP. There's so much we can take for granted.
What we do in BJC when there's infrastructure coding we don't want kids to have to bother with is to provide starter projects with the selectors and constructors, or whatever it is, already defined. Can you not do that?
It's always "functions down to the metal"! Nor is it the case that Snap! presents students with every level of abstraction down to machine language; on the contrary, as a Lisp it's near the peak of very high level languages. So I think your rhetoric is running away with you about that. Even SICP itself doesn't get anywhere near exposing hardware until Chapter 5, although in some places it talks about hardware in the abstract, as in the stuff on critical sections in 3.4. And even in Chapter 5 it's an abstract register machine.
I do think you have a good point, though, about paying more attention to data. I'd be very happy with a block to define an ADT -- Jens will tell you that's my standard example of why we need a DEFINE block -- but I want the resulting constructors and selectors to be ordinary, editable blocks, not quasi-primitive opaque boxes. That's a pedagogic question that doesn't come up in Scheme, in which you can't look inside of even the procedures you define yourself; their expected style of work is that you write the code in Emacs. But we teach kids to expect to be able to look inside of blocks.
(Something we keep talking about doing, but haven't gotten around to yet, is associating with many Snap! primitives, especially the HOFs, a definition of the same block as Snap! code, so you can click "edit" on the context menu of a primitive and get a version that exposes the algorithm.)
So in my view the macrology all happens in NEW DATA TYPE; the selector CRACKLE is just ITEM 2 OF or something. CRACKLE is a plain old, editable, procedure. But you seem to have a different vision. Please help me understand.
Of course it'll be editable! Why wouldn't it be? But it won't replace the primitive, because we don't want to slow down code that uses it. So when you ask to edit MAP, you'll get something like this:
Agree where macrology happens, and what crackle does. I'm still stuck on editable.
If I used a Lisp macro inside some other Lisp form, I'm not sure what it would mean to "edit" the macro expansion. Or, thinking text program file for a moment, if my source code called macro (define-type ....), and I somehow semantically changed the macro-expansion but not the macro definition, which version defines my program?
For a prototype I'd be ok with either. But when macros arrive, would the macro-expansion of new data type be editable? Would the generated blocks be somehow linked to the macro-invocation that spawned them, so that something reasonable could be done if I decide to add or delete a field in that type definition? Or even just re-run the macro invocation. Just want to understand current thinking.
Love the snail
Could I edit map & save it? And if my edit had some semantic difference from the primitive, which one defines my program?
Ah. I think our joint use of the word "macro" may be misleading. NEW DATA TYPE isn't like a traditional Lisp macro that's meant to be called inside a larger expression and expands into code for that expression to run. Instead, NEW DATA TYPE has an effect: It defines new blocks. Once those blocks are defined, they're blocks! So CRACKLE OF THINGY is a block whose body is REPORT (ITEM 2 OF (THINGY)). And there's no such thing in Snap! as a non-editable custom block. As an analogy, when you load a library, even though you're meant to think of the library's blocks as quasi-primitive, they're really custom blocks, and they're editable. If you break one by editing it, then your program won't work, same as if you edit one of your own blocks and break it.
You seem to think that CRACKLE is a macro that generates the ITEM 2 OF on the fly? I don't see how that would work.
Depends which block you use, the primitive (no snail in its name) or the user-code one (with the snail). The main purpose of the snail isn't to tell you it's slow but rather to make it a different title text from the primitive's title text. It could be any symbol that's in Unicode.
We haven't designed any of that yet. Clearly for debugging purposes the user has to be able to see the actual expansion somehow. My offhand first idea is that when you visible-step through an expression with a macro call, when the macro returns a chunk of code to the caller, we'll open a block editor that doesn't allow editing, with the macro call inside a hat block, and the expansion underneath it. And when you leave the macro expansion, that editor disappears.
Yeah, good idea. And create a predicate IS ___ A THINGY? and/or add THINGY to the menu of the IS ___ A [LIST|NUMBER|TEXT|...] ? block.