Unring dropped reporters that aren't using implicit parameters - #3575
gigamonkey wants to merge 1 commit into
Conversation
Dropping a variable getter into a higher-order block's ring hole has always dissolved the ring, on the theory that the variable presumably holds a ringed bit of code. Generalize that theory to other reporters: a reporter dropped into a ring hole with no empty slots isn't using the ring's implicit parameters, so it's presumably computing the function itself (e.g. a custom block that reports a ring) and now unrings the same way. Reporters with empty slots still keep the ring, as does a filled reporter that references one of the ring's declared input names (e.g. dropping ((x) * (2)) into a ring with explicit parameter x). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
I vote no on this. Yes, it makes sense in the context of a list-traversing higher order function, but in other contexts, such as a CASCADE, one might want to use a niladic sensing reporter such as X POSITION in the ring. We could, I suppose, treat MAP, KEEP, and COMBINE differently from RUN, CALL, etc., but I think it's better if the rule is the same for every input of type Reporter. But this isn't a do-or-die issue for me. |
|
I agree with Brian because there are many blocks that have no slots and never return scripts, so it would just make the experience annoying and confusing. However since Snap now has the ability to block drops in inputs based on the return value, you could leverage that mechanism to only have it remove the ring if the return value is a script. |
|
@brianharvey what is CASCADE? I can't find a block with that name. @ego-lay-atman-bay Does it infer the type of the return value or is that a thing the block author has to declare somehow? I'm not super tied to this; it just seemed like a bit of a gap. I feel like maybe there's still some logic that should work for RUN and CALL. Like if I drop a reporter block with no empty holes into a CALL what am I even doing? Why not just use the block directly? Similarly for a command block in RUN. And especially if the CALL or RUN is expecting to provide inputs to the funarg, if the expression has no holes the only way that makes sense is if the expression returns a lambda, right? |
It doesn't look like it's inferred, the block author has to right click the block prototype and select "return data type". It was an option Jens added a while ago because schools wanted it (the whole drop blocking thing is an option you have to enable).
Yeah, the only reason I can really think of is dropping it into ASK or TELL. I think if this drop behavior is restricted to only CALL, RUN, and LAUNCH, it would be fine, though since RUN and LAUNCH don't have reporter rings (they're command rings), they already have this behavior with every reporter. However I still don't think every block should get this treatment, because the size block doesn't report a ring, neither does the tempo block. Does it make sense to put them in a CALL block? No, but it also doesn't make sense to act like they return a ring. It might be good to exclude the primitives and only have this behavior on custom blocks. |
|
CASCADE is in the iteration library; it's like REPEAT for functions, computing f(f(f(f...(x)))). But you're right, it too doesn't really make much sense for niladic functions, although the CASCADE UNTIL variant might be useful for them. Our original reasoning about this dates back to when RUN and CALL were the only primitive higher order procedures, and even things like TELL and ASK were implemented using them. In the beginning we had no default behavior about ringing the inputs to HOFs; we did or didn't put a ring around them depending on how far from the input slot you let go of the mouse button. Everyone hated that; eventually we hit upon putting a visible ring around the (empty) Procedure-type input slots, and making unringifying be the option you had to ask for explicitly. Variable rings are an exception because of the very common special case of dragging the formal parameter of a (custom) HOF into a RUN or CALL in its body. Just between us, the precise semantics of orange variable blobs are hard for new users to understand, e.g., people want to drag them into the first input of SET. So we kind of resigned ourselves to treating them specially, and so we were more open to the idea of also treating them specially when used with RUN or CALL. But dragging other niladic reporters into RUN or CALL isn't something a new user will naturally do without thinking about what it should mean. Users who write niladic reporters that report procedures know enough to know how to unringify one if desired. So I'm inclined to keep the semantics as simple as possible even if I don't have a compelling use case. But, as I say, I'm not adamant about it. |
|
Deciding on whether a reporter has empty slots or not is probably wrong, because in many cases an empty slot assumes some default value, e.g. an empty list, the number zero or an empty text, and in other cases there might be reporters that return a parameterized function based on an explicit value from an outer scope. What we should have, instead, is a way to specify the return data type for reporters. Wait, that's what we have in v12! :D That was, in fact, one of my motivations to introduce type annotations in the first place. I just didn't yet get around to actually using them for the "vanishing ring" gimmick of HoFs. But that's totally my plan. Something I've added in the last minor release (v12.1 ) was an auto-type-annotation feature. Now, when you make or edit a reporter that doesn't yet have a specified return type, that type is inferred automatically by analyzing its function body. As soon as I've finished my current work on the German Snap! curriculum I'll come back and - pull some PRs - continue on this. Then I expect working with HoFs (and metaprogramming in particular) to become less finicky. |
This PR makes using higher-order blocks slightly more convenient.
Dropping a variable getter into a higher-order block's ring hole has always dissolved the ring, on the theory that the variable presumably holds a ringed bit of code. This PR generalizes that to other reporters: a reporter with no empty holes dropped into a ring is presumably computing the function itself (e.g. a custom block that reports a ring) so should be automatically unringed like a dropped variable.
Reporters with empty slots still keep the ring, as does a filled reporter that references one of the ring's declared input names (e.g. dropping ((x) * (2)) into a ring with explicit parameter x).