1

I want to define block and call it in this way:

add := [ :a :b |
    ^(a+b).
].

n := add value: 1 value: 2.

But when I try it, I get an error:

$ gst 3.1.st 
Object: 3 error: return from a dead method context
SystemExceptions.BadReturn(Exception)>>signal (ExcHandling.st:254)
SystemExceptions.BadReturn class(Exception class)>>signal (ExcHandling.st:151)
SmallInteger(Object)>>badReturnError (Object.st:1389)
UndefinedObject>>executeStatements (3.1.st:3)

How can I call a function in GNU Smalltalk?

Well, I dropped the return statement, and this code works fine. But when I try to define more complicated function, for example:

nod := [ :a :b |
    (a=b) ifTrue: [a].
    (a>b) ifTrue: [nod value: (a-b) value: b].
    (a<b) ifTrue: [nod value: a value: (b-a)].
].

n := nod value: 1 value: 2.
n printNl.

It prints nil. And if I define with "early exit":

nod := [ :a :b |
    (a=b) ifTrue: [^a].
    (a>b) ifTrue: [nod value: (a-b) value: b].
    (a<b) ifTrue: [nod value: a value: (b-a)].
].

n := nod value: 1 value: 2.
n printNl.

It gives me the same error: return from a dead method context.

I solve this problem in this way:

nod := [ :a :b |
    (a=b) ifTrue: [
        a
    ] ifFalse: [
        (a>b) ifTrue: [nod value: (a-b) value: b] ifFalse: [nod value: a value: (b-a)].
    ]
].

n := nod value: 1 value: 2.
n printNl.

But I think, it is not beautiful way.

DmitriyM
  • 125
  • 2
  • 9
  • 1
    For the fun of it, you could play with somthing like this http://stackoverflow.com/questions/7547750/smalltalk-block-can-i-explicitly-set-the-returning-value-and-stop-executing-th/11532045#11532045 – aka.nice Apr 04 '16 at 22:23

3 Answers3

5

Drop the return statement (^) from your code and it will work fine.

In smalltalk, returning exits the method in which the return statement appears. It's used for early exits, for example a < b ifTrue: [^a] ifFalse:[^b].

In this case, you don't want the block to exit the containing method, you just want it to evaluate to something when sending it value:value:. A block evaluates to the last statement in it, so just make it a regular statement there and it will do the trick.

The error message you got, by the way, also explains the problem: you're trying to return 3 from a method which is already dead.

Oak
  • 26,231
  • 8
  • 93
  • 152
  • Ohh... Not exactly :( I upgrade my question. – DmitriyM Apr 04 '16 at 06:30
  • 1
    @DmitriyM regarding your edit - the way you tried last is the way to do it, it's definitely the most smalltalk-ish approach. Your attempt with consecutive `ifTrue` statements is incorrect; in fact, the 1st and 2nd blocks in that `nod` implementation have no side effect and so are meaningless. Remember, it's only the last statement which is returned - and the last statement is the last `ifTrue`. – Oak Apr 04 '16 at 07:34
4

Remove non-local return (^), parenthesis, and period inside a block. And try doing it again.

Igor Stasenko
  • 1,037
  • 5
  • 8
3

You've already accepted an answer to your original question, then redefined your question.

To answer your updated question, you could use the fact that a block returns the value of its last statement, and use a local variable:

nod := [ :a :b | |r|
    (a = b) ifTrue: [r := a].
    (a > b) ifTrue: [r := nod value: (a-b) value: b].
    (a < b) ifTrue: [r := nod value: a value: (b-a)].
    r
].

I am curious, though, about the context. I suspect this might more appropriately be defined as a selector/method for a class.


By the way, the above implementation will go into an infinite recursion if either argument is negative.

The simplest way to get the results you're after would be:

nod := [ :a :b | a gcd: b ].

:)

lurker
  • 56,987
  • 9
  • 69
  • 103