Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this code set the variable a to 9?

I am confused by this piece of code:

| a b c| a := 1. b := [a := a + 1]. c := [a := a - 2. b].
        10 timesRepeat: (a even ifTrue: b ifFalse: c). a

My assumption was that this piece of code would set a to -19. Each iteration would test if a was even, but a would be odd so c would be called, substracting 2 from a without affecting its parity. c would not call b because, if my understanding of blocks is correct, the last element of the block is returned instead of evaluated; so c would return b, but timesRepeat discards whatever is returned anyway so this b in c has no effect.

My assumption was proven to be wrong: this piece of code sets a to 9 instead. To see what's going on I modified this piece of code slightly:

| a b c| a := 1. b := [Transcript show: (a displayString). a := a + 1]. c := [Transcript  show: (a displayString). a := a - 2. b.].
           10 timesRepeat: (a even ifTrue: b ifFalse: c). a

This is what gets printed:

1-1012345678

So it would seem b is called after all? Was my assumption wrong that b is returned instead of called?

Let's try to check this:

jkl := [Transcript show: 'I am called too.'].
asdf := [Transcript show: 'I am called!'. jkl].

10 timesRepeat: asdf

Nope, asdf does not call jkl here:

I am called!I am called!I am called!I am called!I am called!I am called!I am called!I am called!I am called!I am called!

And anyway, if c was always just calling b, its effect would be to effectively substract 1 from a; but this doesn't happen. Instead, the first iteration seems to call c and then, mysteriously, each iteration seems to call b instead, even if a is odd!!

What's going on here??


2 Answers

The timesRepeat: selector wants a block as an argument. You are calling it with an expression inside of parentheses:

10 timesRepeat: (a even ifTrue: b ifFalse: c).

It just so happens, though, that c is defined as the block [a := a - 2. b] which returns the value of b and that happens to be a block. So timesRepeat: is happy, and it executes the block b on each iteration in which a is odd. If you write it correctly as:

10 timesRepeat: [a even ifTrue: b ifFalse: c].

Then at the end, a will be -19 as expected.

Regarding your statement: if my understanding of blocks is correct, the last element of the block is returned instead of evaluated, this isn't really the case. There's no special treatment for the last statement in a block other than it's result is indeed returned as the value of the block when that block is executed. Your last statement in the block is just the name of a variable. The value of the variable just happens to be a block, but no matter what it is, just having a variable name by itself as a statement in Smalltalk just returns the value of the variable. If the variable happens to be a block, you get the block. The block is not executed.

Consider the following blocks:

[a := 1. b := 2. c := a+b]

When this block is executed, then a will have the value 1, b the value 2, and c the value 3. The value the block will return is the value of c, which is 3.

[a := 1. b := 2. a]

If you execute this block, the result will be the value of a, which is 1.

[a := 1. b := 2. c := [a+b]. c]

If you execute this block, the result will be the block that the variable c represents. It does not execute the block c. This is consistent with the prior example.

So, when you execute the block, [Transcript show: 'I am called!'. jkl]., the jkl at the end is not executed. It's value is just returned. If you want to execute it, you could write asdf := [Transcript show: 'I am called!'. jkl value]. A block will execute when sent the value message. The result of executing block [Transcript show: 'I am called!'. jkl value]. will be the result of executing the block jkl.

like image 181
lurker Avatar answered Jun 21 '26 23:06

lurker


I may be the only one, but I find the second sentence below a little bit obscure:

It just so happens, though, that c is defined as the block [a := a - 2. b] which returns the value of b and that happens to be a block. So timesRepeat: is happy, and it executes the block b on each iteration in which a is odd.

The semantics of Smalltalk is:

  1. Evaluate the receiver
  2. Evaluate the arguments
  3. Send the message

The order is crucial because evaluations may have side effects.

So, what actually happens is:

  1. Receiver 10 evaluates to 10
  2. Argument a even ifTrue: b ifFalse: c evaluates to b. Side effect a = -1.
  3. The message 10 timesRepeat: b or 10 timesRepeat: [a := a + 1] is sent

Hence, a = 9.

like image 27
Leandro Caniglia Avatar answered Jun 22 '26 00:06

Leandro Caniglia



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!