Discuss Scratch

Jonathan50
Scratcher
1000+ posts

First-class procedures and custom reporters

This isn't an original idea. But there isn't an existing suggestions topic about this, as far as I can tell, so add your support/no support here anyway.

The idea is to add custom reporters, as in Snap!, and first class procedures, as in Snap!. See the Snap! about page.

What is “lambda”?
A “lambda” is an expression that evaluates to a procedure. (If you don't know what “evaluate” means, it means, in the context of Scratch, running a reporter block and all the blocks inside it and reporting the result. 2 + 2 evaluates to 4.) Basically, it lets you put blocks themselves (which can take arguments) into variables, lists, block arguments, etc.

Custom blocks are procedures, but procedures in Scratch are not first class, so in Scratch an expression cannot evaluate to a procedure and you can't set a variable to a procedure or pass a procedure as input to another procedure or add a procedure to a list and so on. Additionally custom blocks always need to be bound to a name, and cannot be nested.

Procedures parameters in Scratch should be lexically scoped, and procedures should be closures (that's how lexically scoped first class procedures are implemented).

Lexical scoping (aka static scoping) means that procedures are in the scope of (i.e. can access) variables that are in the scope of the place where the procedure has been created. This makes procedures that return new procedures very powerful, as the inner procedure can access parameters passed to the outer procedure. Note that each time a custom block is called then a new variable is created for each custom block input. This is also why recursive procedures (procedures that directly or indirectly call themselves) work in Scratch. (That might be confusing because Scratch currently distinguishes “variables” (global/sprite-local variables) and “inputs” (parameters).)

(The name comes from the Lambda calculus.)

How should lambdas look in Scratch?
¯\_(ツ)_/¯
I like Snap!'s rings.

Some things that lambda and custom reporters solve.
  • Object-oriented programming, message passing, encapsulation and inheritance with first-class objects:
    define make a dog (name)
    report ({
    if <(message :: custom-arg) = [name]> then
    report (name) :: custom cap
    end
    if <(message :: custom-arg) = [bark]> then
    say [Woof woof!] for (2) secs
    end
    } input names: (message :: custom-arg) @delInput @addInput :: grey ring) :: custom cap

    define ask (object) for (message)
    report (call (object) with inputs (message) @delInput @addInput :: control) :: custom cap

    define tell (object) to (message)
    run (object) with inputs (message) @delInput @addInput :: control

  • Local variables (lexical scoping)
    (For convenience, Scratch should probably have an easier way to make local variables, but it's possible do it with lambda.)

  • Very versatile procedures (higher order procedures)

  • Callbacks

  • Data-directed programming, a very powerful additive dispatch thing

  • …and much, much more!

But New Scratchers won't understand…
It doesn't matter because New Scratchers don't need to have any prior knowledge of programming when they just get started; that's because they are new. The whole point of learning Scratch is learning.

Ideas for how it could look

Chibi-Matoran wrote:

Another possibility:
(function (x::custom) -> {report(x)::control}::custom) //id
You would be able to add and delete parameters by right-clicking the block, which is truer to Scratch's “custom blocks.”

Chibi-Matoran wrote:

I think the official terminology is too confusing.
(instruction {} with variables +-::ring grey)
(do instruction (instruction{} with variables +-::ring grey) with variable values +-::control)

Jonathan50 wrote:

Chibi-Matoran wrote:

I think the official terminology is too confusing.
...
I don't think it should say ‘variables’ though:
http://i.cubeupload.com/PVxCsB.png

adespotist wrote:

How about this?
the script {move (10) steps}::operators reporter
Obviously because of the limitations of scratchblocks2 I can't do multiple lines of scripts in there but in the real Scratch it would work.

kvackkvack wrote:

This feature is not complicated at all if designed and explained properly.
I'd suggest something like
set [my lambda v] to (the script { move (10) steps } :: operators)
(result of running (my lambda) :: control)
run (my lambda) :: control
I might make a long post trying to explain it after school.

BookOwl wrote:

I like
(the blocks ({report [Hi!] :: custom} ::extension ring ) :: custom)
for lambdas, and
(call (script :: variable) with args [1] [2] :: custom)
run (script :: variable) with args [1] [2] :: custom
for running them

Owengren wrote:

(report script {move (10) steps}::custom-arg ring)
(report reporter (size)::custom-arg ring)
(report boolean <touching [ v]?>::custom-arg ring)

-Io- wrote:

We could always make it a reporter with a c mouth if the ring block is too confusing

Jonathan50 wrote:

Something like
(make procedure {
foo :: grey
bar :: grey
baz :: grey
}:: custom)
(make procedure [] :: custom)
? perhaps there could just be the first variant?

Something like this bad mockup

?

Last edited by Jonathan50 (April 20, 2021 23:30:11)

gdpr533f604550b2f20900645890
Scratcher
1000+ posts

First-class procedures and custom reporters

I support the addition of lambdas, because then we could implement many features by ourselves. However, I feel like your examples are too close to Snap!; perhaps we should develop ways that flow better with Scratch's designs? For example, the arrow inputs are very Snap!-ish. I don't want Scratch to start copying Snap!'s designs.

I support overall, but want lambdas to be presented differently, so they are easier to understand. We should brainstorm possibilities.
gdpr533f604550b2f20900645890
Scratcher
1000+ posts

First-class procedures and custom reporters

Another possibility:
(function (x::custom) -> {report(x)::control}::custom) //id
You would be able to add and delete parameters by right-clicking the block, which is truer to Scratch's “custom blocks.”
Jonathan50
Scratcher
1000+ posts

First-class procedures and custom reporters

Chibi-Matoran wrote:

I support the addition of lambdas, because then we could implement many features by ourselves. However, I feel like your examples are too close to Snap!; perhaps we should develop ways that flow better with Scratch's designs? For example, the arrow inputs are very Snap!-ish. I don't want Scratch to start copying Snap!'s designs.

I support overall, but want lambdas to be presented differently, so they are easier to understand. We should brainstorm possibilities.
¯\_(ツ)_/¯ If you can think of something I'll add it to the OP.
Jonathan50
Scratcher
1000+ posts

First-class procedures and custom reporters

(λ (x :: custom-arg) (y :: custom-arg) -> ((x) + (y)) :: custom)
(λ (x :: custom-arg) (y :: custom-arg) → ((x) + (y)) :: custom)
(procedure (x :: custom-arg) (y :: custom-arg) → ((x) + (y)) :: custom)
(function (x :: custom-arg) (y :: custom-arg) → ((x) + (y)) :: custom)
gdpr533f604550b2f20900645890
Scratcher
1000+ posts

First-class procedures and custom reporters

One flaw with Snap! is that it differentiates between named functions and lambdas, even though they are the same. In an ideal world, custom blocks and rings should be called the same way.

In Scratch, lambdas could be represented with first-class “define” heads. Scratch would have an anonymous version of the “define” block.
set [lambda v] to {define (x)(y)(z)}

EDIT: Jonathan50, I don't think we should use the lambda symbol. “Procedure” isn't the correct word for something that returns a value.

Last edited by gdpr533f604550b2f20900645890 (April 25, 2016 23:58:56)

Jonathan50
Scratcher
1000+ posts

First-class procedures and custom reporters

Chibi-Matoran wrote:

One flaw with Snap! is that it differentiates between named functions and lambdas, even though they are the same. In an ideal world, custom blocks and rings should be called the same way.
Hm…

I'm not sure. But this loses Scratch's amazing Smalltalk-style block names interleaved with inputs.

Last edited by Jonathan50 (Aug. 15, 2016 02:23:48)

Jonathan50
Scratcher
1000+ posts

First-class procedures and custom reporters

Chibi-Matoran wrote:

EDIT: Jonathan50, I don't think we should use the lambda symbol. “Procedure” isn't the correct word for something that returns a value.
Actually, it is:
define my procedure
change [global variable v] by (20) // Side-effect! This is not a function, despite returning a value.
say [Hello!] for (2) secs // More side-effects.
report [foo] :: control cap

define my function
report [bar] :: control cap // As this has no side-effects and returns a value, it is a function *and* a procedure.

define ignore (input) // Despite having no side-effects, this block does not return a value and is therefore not a function.
define my other procedure
ignore (my function :: custom) // Ditto

Last edited by Jonathan50 (April 26, 2016 00:10:19)

gdpr533f604550b2f20900645890
Scratcher
1000+ posts

First-class procedures and custom reporters

Oh, okay. I always considered pieces of code which made side effects, then returned a value to be function/procedure hybrids. I think you're right.
-Io-
Scratcher
1000+ posts

First-class procedures and custom reporters

Support!

I very much prefer the ring like blocks
I think the worded blocks are more adequate for Scratch's design. And “function” might be better too.
Jonathan50
Scratcher
1000+ posts

First-class procedures and custom reporters

-Io- wrote:

Support!

I very much prefer the ring like blocks
I think the worded blocks are more adequate for Scratch's design. And “function” might be better too.
…But
(function {say [hi]} :: grey ring) // Lies! This isn't a function
gdpr533f604550b2f20900645890
Scratcher
1000+ posts

First-class procedures and custom reporters

I think the official terminology is too confusing.
(instruction {} with variables +-::ring grey)
(do instruction (instruction{} with variables +-::ring grey) with variable values +-::control)

Last edited by gdpr533f604550b2f20900645890 (April 26, 2016 01:59:25)

Jonathan50
Scratcher
1000+ posts

First-class procedures and custom reporters

Chibi-Matoran wrote:

I think the official terminology is too confusing.
(instruction {} with variables +-::ring grey)
(do instruction (instruction{} with variables +-::ring grey) with variable values +-::control)
I don't think it should say ‘variables’ though:
-Io-
Scratcher
1000+ posts

First-class procedures and custom reporters

Jonathan50 wrote:

-Io- wrote:

Support!

I very much prefer the ring like blocks
I think the worded blocks are more adequate for Scratch's design. And “function” might be better too.
…But
(function {say [hi]} :: grey ring) // Lies! This isn't a function
Hmm you're right.

Though, is there any other terminology that will not confuse a bit, or isn't too long?
Jonathan50
Scratcher
1000+ posts

First-class procedures and custom reporters

-Io- wrote:

Hmm you're right.

Though, is there any other terminology that will not confuse a bit, or isn't too long?
Procedure? Often abbreviated to “proc” but “proc” is less self-explanatory and Scratch typically doesn't use abbreviations.
-Io-
Scratcher
1000+ posts

First-class procedures and custom reporters

Jonathan50 wrote:

-Io- wrote:

Hmm you're right.

Though, is there any other terminology that will not confuse a bit, or isn't too long?
Procedure? Often abbreviated to “proc” but “proc” is less self-explanatory and Scratch typically doesn't use abbreviations.
Maybe…

Problem is, stack ring blocks can be procedures or functions.
We could call stack ring blocks procedures, and the other two functions.
adespotist
New Scratcher
95 posts

First-class procedures and custom reporters

iamunknown2
Scratcher
1000+ posts

First-class procedures and custom reporters

Not very good at complex data structures, but is there a difference between a function and a procedure?

Also, what are the advantages of lambdas (in the round block version)? It seems that the main use of lambdas is so that creating a function is unnecessary for parameters expecting a function.

Last edited by iamunknown2 (April 26, 2016 13:43:54)

adespotist
New Scratcher
95 posts

First-class procedures and custom reporters

iamunknown2 wrote:

Not very good at complex data structures, but is there a difference between a function and a procedure?
Generally speaking, a function is something that returns a value (think the JOIN block) and a procedure is something that doesn't (think the MOVE block)

Also, what are the advantages of lambdas (in the round block version)? It seems that the main use of lambdas is so that creating a function is unnecessary for parameters expecting a function.
Think of a number. Anywhere you can use a number you can also use a lambda. Not only can you just pass them to functions, you can store them in lists, store them in variables, return them through functions, anything you can think of. That's what it means to be first class.

Last edited by adespotist (April 26, 2016 13:51:03)

adespotist
New Scratcher
95 posts

First-class procedures and custom reporters

-Io- wrote:

Support!

I very much prefer the ring like blocks
I think the worded blocks are more adequate for Scratch's design. And “function” might be better too.
How about this?
the script {move (10) steps}::operators reporter
Obviously because of the limitations of scratchblocks2 I can't do multiple lines of scripts in there but in the real Scratch it would work.

Last edited by adespotist (April 26, 2016 13:54:10)

Powered by DjangoBB