i never said anything about functions or suggested that was how it should work, and my statement about the distinction was that i don't understand why they behave differently in this scenario when they behave nearly identically in just about every other scenario.
it seems weird to me to use this much jargon when trying to talk about how kids think about programming because they simply don't have these words in the first place. i intentionally try to avoid it when talking about or designing things, even for particularly advanced topics.
my mental model on the entirety of scratch block shapes was "blocks have different shapes because they're used for different things". it wouldn't make sense to put a round block in the middle of a stack because it wouldn't do anything. it wouldn't make sense to put a stack block in a round slot because there's nothing for you to take from it. mixing text, numbers, booleans, and variables is iffy but that's not much.
looking at snap for the first time, even after working with and digging into a variety of programming languages, was baffling to me. like i described, rings can contain stack blocks or reporters, custom reporters are built from stack blocks, many reporters do actions by themselves even when ignoring the value, etc.
the scratch block shapes are there, but in every immediately obvious way, it behaves like functions.
whatever semantic distinctions are actually buried in there simply do not matter. at the point we can get specific enough to see behavioral differences between commands and reporters, it's already too specific and in depth to (in most cases) make comparisons to other languages or talk about how it affects learning. the current difference between commands and reporters in snap is the block shape, and subtle details.
until now i didn't think of hyperization having anything to do with this command/reporter boundary, because why would it? the list goes in a slot, and then the block does its thing for every item in the list at once.
"(reporter ring) for each in (list)", or "(reporter ring) from each in (list)".
the four blocks that already mutate lists are stack blocks, and are very clear in both wording and shape that they change the list.
????????????????????????
snap has an incredible number of options, many rarely used, as far as i can tell absolutely everything is hooked into the latest possible points at runtime, it's been repeatedly demonstrated to be too slow for many projects that a wide variety of people create even on powerful systems, and as far as i can tell not a single person, not even you or jens, can keep track of how it all behaves.
the vast majority of time snap spends in every scenario is spent just checking the states of things at any given time because the code is all tangled and everything can access and change everything else with no documentation on what does what.
jens has repeatedly very loudly and rudely complained about metaprogramming, and proceeded to add far far more complex runtime metaprogramming.
snap spends most of the time running a project just parsing the next block to figure out what it's supposed to do. normally this kind of thing would be done beforehand, but who knows what would break if you did that.
(ignore that quicksteps broke tons of projects, continues to be a major footgun, and as far as i can tell has no option to turn off)
i have spent actual days writing out just the surface level of problems to help people optimize projects recently.
i've tried to submit a patch, had it long ignored, complained about, begrudgingly put in far later with no opportunity for me to review it, and then entirely thrown out days later for an easy to spot single letter typo.
i spent the entire month of november writing patches for myself with a complex and cumbersome dependency system to hopefully work across updates (again, everything in snap is connected to everything else) after encountering horrendous lag on a project that had literally nothing but one large costume.
i don't understand how you could think that snap is in any way slim or fast.
Yeah, okay, you're right, I haven't totally thought through what hyperized TELL should do. I'm not even sure whether TELL should RUN or LAUNCH the script. I kind of lean toward LAUNCH because that lets me think of the script as happening entirely under the purview of the target sprite, rather than as me (the current sprite) running code on behalf of someone else.
One of the ways in which we've tried to simplify OOP is to handwave away the distinction between messages and methods. In Snap! sprite OOP, the message is the method. Maybe that's the source of some confusion here.
May I say, to start with, that it's not much fun reading a message that's so hostile. I try to be pretty thick-skinned, and you'll notice that I'm not yelling at you or ignoring you, but it's hard.
You suggested that commands and reporters are overlapping in meaning, and that's true in some ways but I think not in all ways. I tried to explain why we find it worthwhile to distinguish the two.
There's a difference between a discussion among language developers and a tutorial for children. I thought, from your previous message, that you wanted to be treated as an adult, not as a child. I'm not being sarcastic! For example, when @mr_owlssssnap2 said
a few messages back, he was asking to be treated as a student, not as a language developer. Language developers, these days, all know what MAP is. When he said, later in the same message,
he was asking to be talked with as a language developer. That's why I gave him two separate answers.
And the distinction isn't just about vocabulary. It's about an assumed shared background of ideas, so for example I can say "tail call elimination" (or even "TCE") in an email to Jens and trust that the picture in his head is the same as the picture in mine. And I can say "TCE makes it hard to give transparent error messages" and that calls to mind a whole series of conversations going back to at least 1980.
We try to keep reporters functional, and to give commands effects. I'm sure we don't always get it right. But, for example, about "anything can go in a ring," I suppose we could have different colors for command rings and reporter rings. Instead we try to distinguish them more subtly by their interior shape: .
You're absolutely right that there is some similarity in meaning between the expression
and the script .
They're not the same, but they're similar in many ways, perhaps confusingly similar.
The thing is, one of our design goals, so basic that we rarely feel a need to say it out loud, is "make easy things easy and hard things possible." With respect to the issues under discussion here, that means that in some contexts, where it's crystal clear what the user means to do, we can be a little handwavy about what REPORT does, and why it's a command rather than a reporter, and whether you can put one in a reporter-shaped ring. In other contexts, where it's not so easy for us to read the user's mind, we have to keep the distinction clear.
It's very likely that we haven't yet found the very best possible way to satisfy all of the contradictory goals in building a language for learners. I don't feel a need to get defensive about every detail. But, by the same token, you should bear in mind that we've been thinking about these issues for a long time now, and we're not stupid, or at least Jens and Bernat and Michael and Jadga and Joan aren't stupid.
It's clear to you, but trust me, it's not clear to bunches of high school computer science teachers, let alone their students. So we try to have redundant sources of information, in this case the block's shape and its title text. (And yes, we get sloppy sometimes; I don't think we're yet 100% consistent about whether predicate blocks have question marks in their names.)
See, this isn't fun to read. There isn't a lot of internal program documentation, but there isn't none either.
I'll let Jens speak to the speed of Snap! if he feels like it. (For the kind of projects I like to write, speed isn't that important.)
i, as an adult, try my best to talk about design for children in the terms of children. i'm not calling it jargon to say it's impossible to understand, or that it's not useful. i'm calling it jargon because only experienced programmers would know the particular abstract concepts that those words describe.
it's likely i completely misunderstood what you meant when talking about the command/reporter distinction making things easier to understand. the distinction didn't help me when i used snap, i don't know what exactly the received wisdom is or how it was received, and i don't know how to make any conclusions about how children understand programs from this terminology.
i don't use those kinds of words and reasoning in this kind of context, and i don't see the decisions made from it working. it is weird to me. i then described how i think about it and my genuine experience with it, because if we're not thinking about these things in the same way, the best i can do is tell you what thoughts i have and how i got there.
i don't particularly like being called hostile when i'm making my best effort to understand you, explain where i get stuck, and explain my own thought process at various points in time.
thanks jens. what does real analysis look like? since helping others speed up projects based on months of analysis isn't enough.
what i've tried to explain is that commands and reporters as they exist in snap appear to have little to no distinction when coming from scratch. in scratch, every individual block has a place where it makes sense to go, and a shape that fits it, with other combinations both impossible and nonsensical.
yes, reporters report values while commands don't, but the actual labelled behavior of individual blocks in snap and new places you can put them don't make the concrete distinction that scratch does.
i still can't tell if you aren't fully understanding what i'm talking about or just glossing over it for brevity or to get to some other subject or what.
what are you trying to point out? i said the blocks are well designed and that helps carry over to the other blocks. i'm not trying to patronize programmers that don't understand it, and i can still imagine how someone might not. maybe i'm wrong about this, but i think that by the time someone gets to MAP they would already be experienced enough to understand what the list mutating blocks do.
i'll admit guessing at what kids think isn't a good habit. do you have notes on how teachers, students, or other kids interact with snap or other programming languages? i haven't found much on the subject and i'd love to read up on it.
that section is somewhat hostile. it's a sore subject after seeing jens repeatedly badger multiple people and sticking his fingers in his ears and whining about "pseudo analysis".
To do a good job designing a language for learners, you have to both think like a child and think like a professional programmer. Even for Scratch, which is very much a language for children (which I don't mean as an insult; that's hard to do!), if you look at the early design papers the Scratch Team wrote for adults, they don't talk like learners; they talk like the sophisticated computer scientists they are. They talk about learners, but when kids make design suggestions, which they do all the time, in their private sanctum the ST are as likely to think "what is this kid misunderstanding, that leads them to make this strange request?" Not always; kids can have good ideas too. But, for example, when @mr_owlssssnap2 (sorry to keep picking on you!) says
I think "Poor kid, someone has taught him about sequences and loops, and now he's struggling to wrap his mind around functional programming" and so, when he suggests
(map ({} @addInput) over @list @delInput mutate? <t> ::list)
I understand it as a sign that he hasn't bought into the goal of teaching people to think functionally.
So, yes, the jargon is for the adult designer conversation, not for the beginner conversation.
This particular discussion is problematic because the OP is a beginner, so all this is going over their head. I think I'm going to split this thread so the design discussion doesn't get in the way of answering the original question.
In Scratch, all reporters are primitive; you can't write a script that reports. (In the beginning, you couldn't even write a command block, and when they did decide to dip their toes in the water, they limited custom blocks to being commands.) So, yes, I agree that once you can write scripts that report, you have to wrap your mind around the possibly confusing idea that a script can be a sequence of commands (including a REPORT) on the inside, but act as a reporter on the outside. That's a hard idea, and it's no surprise that lots of people have trouble with it. The particular surface syntax that you're complaining about (a sequence of blocks inside a reporter-flavored ring) is the way that this hard idea manifests itself in the arena of user-directed messaging. Earlier in this conversation, I was insisting on the apparent quibble that the REPORT block doesn't report a value, because that's something you have to understand--or at least, once the question occurs to you, you won't be happy until you understand the answer.
Python, iirc, deals with this issue by restricting lambda expressions to one-liners whose body is an expression rather than what we'd call a script. That's a way of protecting users from this hard idea, at the cost of drastically reducing the range of things you can do with an anonymous procedure.
May I make a detour into a different but related example? When Jens and I started collaborating on BYOB, the first technical paper we wrote posed the question of whether we could make computer science appeal to math-phobic teenagers using a familiar block-based language, but with advanced ideas snuck in. That's a kid-centric question, and it has no CS jargon in it. But the answer revolves around the fact that lambda expressions are a Turing-equivalent language all by themselves. We don't talk to kids about Turing machines, and we try to present higher order functions in a way that glides past the problem of lambda expressions by (1) reducing them to a visually simple ring, and (2) using empty input slots in place of formal parameters. So you can see something like
and (we hope) get it even if you haven't been to high school and don't know what a function is. The way we talk about it to kids is "MAP puts each item of the list into the empty slot, collecting the results in a new list." But the way we talk about it to adults (such as the Scratch team, in our unsuccessful effort to persuade them to put our ideas into Scratch) is "the ring is equivalent to
(lambda (x) (sqrt x))
using the empty slot as an implicit formal parameter."
Now, maybe the ST was right to reject our idea; maybe you do have to have taken algebra (or, in Piagetian terms, maybe you have to be formal operational*) to get it. My impression, from teaching fifth graders, is that at least some of them can get it, at least in easy cases like the one above. In adult terms, I believe that we've succeeded at teaching kids to be able to use higher order functions without necessarily understanding what a ringed expression or script means outside of the context of a HOF.
* "Piaget found that Swiss children become formal operational at around age 12. There's less evidence that American kids ever become formal operational." --Alan Kay
Speaking as a computer programmer, I find the command/reporter distinction a royal pain in the butt. It means we have to have two versions of a bunch of blocks:
But speaking as a teacher, we have decades of experience showing that it's important for kids to understand the difference, even if, as in Logo, there is no syntactic marker for it.
I still don't see this. To me the difference between an instruction (a command block with its inputs filled in) and an expression (a reporter block with its inputs ditto) is obvious: the expression has a value; the instruction has an action. The subtle details are in the places where this distinction doesn't work.
Well, you can get away with "at once" if you're doing functional programming (no command blocks, except maybe inside the implementation of a reporter), so it doesn't matter what happens when. But, as @mark4sisb points out, in response to me saying the same thing you said, the desired results aren't so obvious once you introduce effects that may change the state of the computation.
The first of those is too easy to confuse with the FOR EACH ITEM block. But the second one is an interesting possibility. Thank you.
Oh, Jens can. I am only an egg. So I'm not going to get in the middle of this thing between you and Jens.
I'm going to speak for myself when I first started Snap!: the mutation of a list using the add, replace, insert, and delete blocks seemed like magic to me.
Long explanation of how I thought the list commands worked and the problems I had
I thought that maybe, behind the scenes, Snap! was doing something like this:
The list input here is of type "any (unevaluated)". In this example, the block essentially detects the variable you inputted in the list slot and changes its value. Of course, if you give it anything other than a variable, it errors (ignore the set blocks, I forgot to remove them for the screenshots):
Similarly, I thought the primitive would do nothing if given a list reporter or some other block that reports a list. Why should it do anything, seeing as there was no place in storage (a variable) for the list to mutate or be retrieved?
In the middle of a larger project, I created this block for use:
Of course, I never expected it to mutate the list it was given because the variable fed to the replace command was not the caller's variable. I had no notion of two variable containing the same list.
Here is something else I did not realize: the list commands could operate on lists inside of lists. Once again, I thought it was limited to just variables.
Combine the two of these misunderstandings, and you get a very confusing problem (this was not the real script, my actual program was much more complicated):
In this example, I gave the inputs "jump", "linguini", and "time". However, my output has "time" twice and "jump" never shows up. So, when debugging, I was very confused: even if the replace item reporter was somehow mutating the list, it should only have changed the range variable, especially because the range variable was only one of the items in the list variable. There was simply no way that this made any sense. I concluded Snap! had a bug and ended up reinitializing my list variable every iteration of the loop. And I moved on.
In any case, it was about a year before I read this topic and learned what I was doing wrong and how to combat it (id of). I used map TONS during that time, especially because it didn't seem to be affected by the "bug" in list mutation.
Personally, I meant that I would've (and have) made a block that is essentially MAP but worse repace each item in @list using ({replace item (index) of (list) with ((content) + (1))} with inputs (list) (index) (content) @delInput @addInput )::list that utilizes a for each loop inside (making the markdown for this block probably took longer than just making othe block would've)
Strangely enough, I've used MAP in JavaScript a few times, but I never bothered to try it in Snap! (they also appear to operate differently, though I greatly prefer how Snap does it)
i wasn't thinking of reference semantics when i made that point, entirely fair. my mind was on other things like how i went about learning various blocks in scratch, and in scratch lists are a seperate type of variable, with the mutation blocks using dropdowns.
i've always thought of that as a confusing point of how the lists themselves work, not the blocks for mutating them, but it's really the same thing.
too large to properly reply to everything here, but i feel like you're still not hearing what i'm actually saying.
talking isn't thinking. it's hard enough to convey one perspective correctly and near impossible to convey it in a way everyone can understand. talking through every possible perspective is just absurd.
i don't think like a kid, and unfortunately i'm not sure i can. what i can do, and is useful in more contexts than just learning, is to use words and analogies that are as simple and accurate as possible.
i see tons and tons of experienced adult programmers not just getting stuck on, but genuinely angry over new terminology because it regularly gets explained very poorly. most people confused about monads or lifetimes already have an intuitive knowledge of the underlying concepts but don't hear the words for it until they get into a particularly confusing and awful situation.
everything you're saying about reporters seems to be built on the assumption that being able to compose a reporter out of commands is surface syntax, a visual quirk that doesn't affect the important preserved distinction of commands and reporters, and that i don't understand that the current model accomplishes this.
again, my primary point is that in snap, reporters often have side effects that make them useful even while ignoring what they report, while in scratch, reporters have no possible use other than the value you get from them.
i'm well aware that scratch only has primitive reporters, but that doesn't detract from my point. if scratch allowed you to compose new reporters out of its existing reporters, and only the reporters, then this would still apply.
these aren't true, and this is exactly my point!
lambda expressions have no side effects, snap reporters do. if you use the NEW CLONE reporter, and do nothing with the output, the clone still exists, and this is exactly my problem with how the distinction works. you only need the second clone block because you can't easily throw the NEW CLONE reporter into a stack.
snap also has another mechanism for providing values from a block that most languages don't have, upvars! consider this block:
all the functionality of both existing clone blocks, rolled into one. the only difference is the shape, and subtle details.
command blocks have instructions AND values, reporters have values AND instructions.
How I speak to users is very different from how I speak to, for example, Jens. You'll never hear me say "tail call elimination" to a novice user! Not even "anonymous procedure." But I've been considering this as a conversation among experienced programmers.
Right, that's true, if by "lambda expressions" you mean "Lambda Calculus lambda expressions." But it's not true if you mean "Lisp lambda expressions," which is the model we've followed. We aren't a pure functional language; we're a multiparadigm language. I think history shows that that's been a good design choice for Lisp (specifically, for Scheme).