Running a lambda w/ report

Link to example project.

I do not know whether this behavior was by design, but ideally a lambda should not interfere with the control flow of the function that calls it.

The demo project isolates the "bug" and a fairly inelegant workaround.

I'm not sure that this is a bug, although I'm open to persuasion.

In lambda calculus, all procedures are functions -- they return values. This is also the case in Lisp and the functional languages. But Snap!, following in the Logo tradition, has commands and reporters. Thus we have RUN and CALL, which have basically the same purpose, but are subtly different.

So, in your first example, you are using RUN to invoke a lambda expression that includes a REPORT. RUN expects a command, i.e., something that doesn't report a value. So, technically, your case A is a user error. But rather than report the error and red-halo the offending script, we try to make sense of this case, assuming that the user probably meant something. Namely, the REPORT block reports a value from the dynamically nearest reporter invocation, which in this case is the call of A. This works as a sort of cheap and dirty macro facility for the specific case of determining the return value from a reporter.

We could instead just ignore the REPORT, as you suggest, but really I'd rather signal an error than silently ignore something the user tried to do.

I, too, am reluctant to call it a bug. The moral of the demo project is that the user should always pass the proper kind of lambda expression.

My troubles with RUN were very niche and basically a result of being lazy. :slight_smile: As with any design feature, I bet that careful deliberation went into the design of Snap!'s RUN and CALL. It's just that to me, the whole thing with REPORT feels a bit wrong.

Yeah. It's commands and reporters that are wrong, but the distinction really works well in a visual language.

I think this is a bug, or at least a very curious choice. To me it's not that the run does something with the value - it's that the report effectively stops execution of itself (as it should) and what seems like 1 frame above.

Here's a simpler case. I don't see why wrapping a report in a custom block or an extra lambda should change things.


The actual REPORT block inside your gray "report 5" reporter has a reporter context within the scope of the RUN from which to report a value. In the second example, the REPORT block is in a command context (the input to RUN), and so it has to find an outer reporter context to report from.

Arguably, your first example is also a user error, and they both should give error messages. In Logo the message would be "You don't say what to do with 5."

Some functions can have side effects and return a value. In C-style languages, you can ignore a return result. In OCaml, all functions return something and you can ignore their result using the semicolon construct. Even Scheme works something like this. I don't think Snap! reflects the semantics of most programming languages (including functional ones) here.


Here's one reason for the current behavior. This is a perfectly sensible thing for a user to do, but the user doesn't (or shouldn't have to) know that FOR is written in Snap! using RUN. If we did what you want, this wouldn't work.

Once we have macros, we could write a more complicated FOR that injects the REPORT into the caller's environment, but arguably that isn't the most pedagogically valuable solution.


Oh, right! OK. I get why the contexts work the way to do -- for some reason, though, I except different results between a custom block and a script on a sprite/stage.

This is one of those things like piano tuning, multiple inheritance, and voting systems: There's provably no way to get it right in every dimension. So we look for common situations, and users complain, and we fine-tune.

Can you point me to literature on there being provably no way to get multiple inheritance right in every dimension? I'm curious =)

Not offhand, but I can tell you what the issue is. Suppose you have parents A and B, in that priority order. You get a message FOO. You don't have a method for it. B does have a method. A doesn't have one itself, but A's parent does. Which method do you run, the one you inherit indirectly via your higher priority parent, or the one you inherit directly from the lower priority parent? There are use cases in which each of those is ("obviously") the right thing.

Mmm, thanks. I've predictably developed an opinion on the right way to solve this, but I'll shush now so that you can get back to discussing the topic at hand.

No, that's okay, I think we've actually exhausted the original topic. (Or I've just exhausted Michael and Aspiring by talking too much. :slight_smile:)

But shouldn't
be the same as

No, because they can't be used in the same context.

You can't fit the report into an input slot. So there is a difference.

I don’t feel like that answers the question.

If you click both scripts, you get the same answer. The only reason you can’t drag the block in there is because of type conventions.

Not type, exactly. Commands don't report a value, so they aren't of any type.

I think we are starting to go in circles. Again, this is all very clean in Scheme because every procedure returns a value, so anything can be put into any context. (As I always used to say in 61A, "Stop asking 'can X be used in Y?' because the answer is always yes!") Of course there could be a domain (type) error, but not a syntactic error, or any such kind of category error.

Once you have commands and reporters in your language, everything gets much messier. So for example, in Logo:

? to foo
> output output 3
> end
? foo
output didn't output to output in foo

Nice error message, eh? Luckily nobody ever does that except in a spirit of "I know this is wrong, but what will Logo do?"

Because of the FOR example, we decided that even though that code is technically incorrect, we should have it REPORT from the innermost context in which REPORTing is appropriate.

But when you click on a report script, the IDE shows you it's value as a reporter would. I think that's useful, but from a student's perspective it can make Snap! seem weird.

I also don't get why returning "nothing" makes things messier. "Nothing" is a perfectly valid type. Snap! doesn't really have an explicit "nothing" type, but most languages do. Javascript even has two! :joy:

Soooo, that's interesting wording. "technically incorrect" -- I can kind of see that. I'm assuming you mean that if Snap! didn't do anything special, you could just never report false? Ok, I get that. But then when you say "the context which is appropriate", I then feel like the current behavior for a "top-level" script is a bug because that doesn't seem like what the programmer indented to happen.

I guess, there's not really a way to disambiguate between the two cases.

What I mean is that we'd be entitled to give an error message. But if you're a user, instead of an implementor or a lambda calculus nut, you don't think of the script in the C slot as being separate from the script containing the FOR. And you'd be right if it were a REPEAT UNTIL or something, because that's a special form, and doesn't establish an inner environment. So we want FOR to behave the same as the other looping blocks.