The implementation of Integer>>#factorial in Pharo is:
factorial
"Answer the factorial of the receiver."
self = 0 ifTrue: [^ 1].
self > 0 ifTrue: [^ self * (self - 1) factorial].
self error: 'Not valid for negative integers'
This a tail-recursive definition. However, I can evaluate 10000 factorial without error in the workspace.
Does Pharo perform tail-call optimisation in any circumstances, is it doing some other optimisation, or is it just using a really deep stack?
It's a really deep stack. Or rather, no stack at all.
Pharo is a descendent of Squeak, which inherits its execution semantics directly from Smalltalk-80. There is no linear fixed-size stack, instead every method call creates a new MethodContext object which provides the space for arguments and temporary variables in each recursive call. It also points to the sending context (for later return) creating a linked list of contexts (which is displayed just like a stack in the debugger). Context objects are allocated on the heap just like any other object. That means call chains can be very deep, since all available memory can be used. You can inspect thisContext to see the currently active method context.
Allocating all these context objects is expensive. For speed, modern VMs (such as the Cog VM used in Pharo) do actually use a stack internally, which consists of linked pages, so it can be arbitrarily large as well. The context objects are only created on demand (e.g. while debugging) and refer to the hidden stack frames and vice versa. This machinery behind the scenes is quite complex, but fortunately hidden from the Smalltalk programmer.
There is no mystery in the execution model of Pharo. The recursive fragment
^ self * (self - 1) factorial
that happens inside the second ifTrue: compiles to the following sequence of bytecodes:
39 <70> self ; receiver of outer message *
40 <70> self ; receiver of inner message -
41 <76> pushConstant: 1 ; argument of self - 1
42 <B1> send: - ; subtract
43 <D0> send: factorial ; send factorial (nothing special here!)
44 <B8> send: * ; multiply
45 <7C> returnTop ; return
Note that in line 43 nothing special happens. The code just sends factorial in the same way it would, had the selector been any other. In particular we can see that there is no special manipulation of the stack here.
This doesn't mean that there cannot be optimizations in the underlying native code. But that is a different discussion. It is the execution model the one that matters to the programmer because any optimization underneath bytecodes is meant to support this model at the conceptual level.
UPDATE
Interestingly, the non-recursive version
factorial2
| f |
f := 1.
2 to: self do: [:i | f := f * i].
^f
is a little bit slower that the recursive one (Pharo). The reason must be that the overhead associated to increasing i is a little bit greater than the recursive send mechanism.
Here are the expressions I tried:
[25000 factorial] timeToRun
[25000 factorial2] timeToRun
IMHO, the initial code that is presumed to have a tail-recursive call to factorial
factorial
"Answer the factorial of the receiver."
self = 0 ifTrue: [^ 1].
self > 0 ifTrue: [^ self * (self - 1) factorial].
self error: 'Not valid for negative integers'
it isn't, actually. The bytecode reported by Leandro's reply proofs that:
39 <70> self ; receiver of outer message *
40 <70> self ; receiver of inner message -
41 <76> pushConstant: 1 ; argument of self - 1
42 <B1> send: - ; subtract
43 <D0> send: factorial ; send factorial (nothing special here!)
44 <B8> send: * ; multiply
45 <7C> returnTop ; return
before the returnTop there is a send of * instead of factorial. I would have written a message using an accumulator as
factorial: acc
^ self = 0
ifTrue: [ acc ]
ifFalse: [ self - 1 factorial: acc * self ]
that produces the bytecode reported in this picture.
Btw,
n := 10000.
[n slowFactorial] timeToRun .
[n factorial] timeToRun.
[n factorial: 1] timeToRun.
both the first and second ones takes 29 millisecs, the last one 595 millisecs on a fresh Pharo 9 image. Why so slow?
No, Pharo and its VM do not optimize recursive tail calls.
It is apparent from running tests on a Pharo 9 image, and this master thesis on the subject confirms that.
As of today Pharo ships with two factorial methods, one (Integer >> factorial) uses a 2-partition algorithm and is the most efficient, the other looks like this:
Integer >> slowFactorial [
self > 0
ifTrue: [ ^ self * (self - 1) factorial ].
self = 0
ifTrue: [ ^ 1 ].
self error: 'Not valid for negative integers'
]
It has an outer recursive structure, but actually still calls the non-recursive factorial method. That probably explains why Massimo Nocentini got nearly identical results when he timed them.
If we try this modified version:
Integer >> recursiveFactorial [
self > 0
ifTrue: [ ^ self * (self - 1) recursiveFactorial ].
self = 0
ifTrue: [ ^ 1 ].
self error: 'Not valid for negative integers'
]
we now have a real recursive method, but, as Massimo pointed out, it's still not tail recursive.
This is tail recursive:
tailRecursiveFactorial: acc
^ self = 0
ifTrue: [ acc ]
ifFalse: [ self - 1 tailRecursiveFactorial: acc * self ]
Without tail call optimization this version shows by far the worst performance, even compared to recursiveFactorial. I think that's because it burdens the stack with all the redundant intermediate results.
Related
I am trying to understand smalltalk. Is it possible to have a standalone method/function, which is not part of any particular class, and which can be called later:
amethod ['amethod called' printNl].
amethod.
Above code gives following error:
simpleclass.st:1: expected Eval, Namespace or class definition
How can I use Eval or Namespace as being suggested by error message?
I tried following but none work:
Eval amethod [...
amethod Eval [...
Eval amethod Eval[... "!"
Eval [... works but I want to give a name to the block so that I can call it later.
Following also works but gets executed immediately and does not execute when called later.
Namespace current: amethod ['amethod called' printNl].
Thanks for your insight.
In Smalltalk the equivalent to a standalone method is a Block (a.k.a. BlockClosure). You create them by enclosing Smalltalk expressions between square brackets. For example
[3 + 4]
To evaluate a block, you send it the message value:
[3 + 4] value
which will answer with 7.
Blocks may also have arguments:
[:s | 3 + s]
you evaluate them with value:
[:s | 3 + s] value: 4 "answers with 7"
If the block has several sentences, you separate them with a dot, as you would do in the body of a method.
Addendum
Blocks in Smalltalk are first class objects. In particular, one can reference them with variables, the same one does with any other objects:
three := 3.
threePlus := [:s | three + s].
for later use
threePlus value: 4 "7"
Blocks can be nested:
random := Random new.
compare := [:p :u | u <= p]
bernoulli60 := [compare value: 0.6 value: random next].
Then the sequence:
bernoulli60 value. "true"
bernoulli60 value. "false"
...
bernoulli60 value. "true"
will answer with true about 60% of the times.
Leandro's answer, altough being correct and with deep smalltalk understanding, is answering what you asked for, but I think, not 100% sure thou, you are actually asking how to "play" around with a code without the need to create a class.
In my eyes want you want is called a Workspace (Smalltalk/X and Dolphin) (it can have different names like Playground in Pharo Smalltalk).
If you want to play around you need to create a local variable.
| result |
result := 0. "Init otherwise nil"
"Adding results of a simple integer factorial"
1 to: 10 do: [ :integer |
result := result + integer factorial
].
Transcript show: result.
Explanation:
I'm using a do: block for 1-10 iterration. (:integer is a block local variable). Next I'm, showing the result on Transcript.
The following piece of code is giving the error error: did not understand '#generality'
pqueue := SortedCollection new.
freqtable keysAndValuesDo: [:key :value |
(value notNil and: [value > 0]) ifTrue: [
|newvalue|
newvalue := Leaf new: key count: value.
pqueue add: newvalue.
]
].
[pqueue size > 1] whileTrue:[
|first second new_internal newcount|
first := pqueue removeFirst.
second := pqueue removeFirst.
first_count := first count.
second_count := second count.
newcount := first_count + second_count.
new_internal := Tree new: nl count: newcount left: first right: second.
pqueue add: new_internal.
].
The inconsistency is in the line pqueue add: new_internal. When I remove this line, the program compiles. I think the problem is related to the iteration block [pqueue size > 1] whileTrue: and pqueue add: new_internal.
Note: This is the algorithm to build the decoding tree based on huffman code.
error-message expanded
Object: $<10> error: did not understand #generality
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
Character(Object)>>doesNotUnderstand: #generality (SysExcept.st:1448)
SmallInteger(Number)>>retryDifferenceCoercing: (Number.st:357)
SmallInteger(Number)>>retryRelationalOp:coercing: (Number.st:295)
SmallInteger>><= (SmallInt.st:215)
Leaf>><= (hzip.st:30)
optimized [] in SortedCollection class>>defaultSortBlock (SortCollect.st:7)
SortedCollection>>insertionIndexFor:upTo: (SortCollect.st:702)
[] in SortedCollection>>merge (SortCollect.st:531)
SortedCollection(SequenceableCollection)>>reverseDo: (SeqCollect.st:958)
SortedCollection>>merge (SortCollect.st:528)
SortedCollection>>beConsistent (SortCollect.st:204)
SortedCollection(OrderedCollection)>>removeFirst (OrderColl.st:295)
optimized [] in UndefinedObject>>executeStatements (hzip.st:156)
BlockClosure>>whileTrue: (BlkClosure.st:328)
UndefinedObject>>executeStatements (hzip.st:154)
Object: $<10> error: did not understand #generality
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
Character(Object)>>doesNotUnderstand: #generality (SysExcept.st:1448)
SmallInteger(Number)>>retryDifferenceCoercing: (Number.st:357)
SmallInteger(Number)>>retryRelationalOp:coercing: (Number.st:295)
SmallInteger>><= (SmallInt.st:215)
Leaf>><= (hzip.st:30)
optimized [] in SortedCollection class>>defaultSortBlock (SortCollect.st:7)
SortedCollection>>insertionIndexFor:upTo: (SortCollect.st:702)
[] in SortedCollection>>merge (SortCollect.st:531)
SortedCollection(SequenceableCollection)>>reverseDo: (SeqCollect.st:958)
SortedCollection>>merge (SortCollect.st:528)
SortedCollection>>beConsistent (SortCollect.st:204)
SortedCollection(OrderedCollection)>>do: (OrderColl.st:64)
UndefinedObject>>executeStatements (hzip.st:164)
One learning we can take from this question is to acquire the habit of reading the stack trace trying to make sense of it. Let's focus in the last few messages:
1. Object: $<10> error: did not understand #generality
2. MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
3. Character(Object)>>doesNotUnderstand: #generality (SysExcept.st:1448)
4. SmallInteger(Number)>>retryDifferenceCoercing: (Number.st:357)
5. SmallInteger(Number)>>retryRelationalOp:coercing: (Number.st:295)
6. SmallInteger>><= (SmallInt.st:215)
7. Leaf>><= (hzip.st:30)
8. optimized [] in SortedCollection class>>defaultSortBlock (SortCollect.st:7)
Each of these lines represents the activation of a method. Every line represents a message and the sequence of messages goes upwards (as it happens in any Stack.) The full detail of every activation can be seen in the debugger. Here, however, we only are presented with the class >> #selector pair. There are several interesting facts we can identify from this summarized information:
In line 1 we get the actual error. In this case we got a MessageNotUnderstood exception. The receiver of the message was the Character $<10>, i.e., the linefeed character.
Lines 2 and 3 confirm that the not understood message was #generality.
Lines 4, 5 and 6 show the progression of messages that ended up sending #generality to the wrong object (linefeed). While 4 and 5 might look obscure for the non-experienced Smalltalker, line 6 has the key information: some SmallInteger received the <= message. This message would fail because the argument wasn't the appropriate one. From the information we already got we know that the argument was the linefeed character.
Line 7 shows that SmallInteger >> #<= came from the way the same selector #<= is implemented in Leaf. It tells us that a Leaf delegates #<= to some Integer known to it.
Line 8 says why we are dealing with the comparison selector #<=. The reason is that we are sorting some collection.
So, we are trying to sort a collection of Leaf objects which rely on some integers for their comparison and somehow one of those "integers" wasn't a Number but the Character linefeed.
If we take a look at the Smalltalk code with this information in mind we see:
The SortedCollection is pqueue and the Leaf objects are the items being added to it.
The invariant property of a SortedCollection is that it always has its elements ordered by a given criterion. Consequently, every time we add: an element to it, the element will be inserted in the correct position. Hence the comparison message #<=.
Now let's look for #add: in the code. Besides of the one above, there is another below:
new_internal := Tree new: nl count: newcount left: first right: second.
pqueue add: new_internal.
This one is interesting because is where the error happens. Note however that we are not adding a Leaf here but a Tree. But wait, it might be that a Tree and a Leaf belong to the same hierarchy. In fact, both Tree and Leaf represent nodes in an acyclic graph. Moreover, the code confirms this idea when it reads:
Leaf new: key count: value.
...
Tree new: nl count: newcount left: first right: second.
See? Both Leaf and Tree have some key (the argument of new:) and some count. In addition, Trees have left and right branches, which Leafs not (of course!)
So, in principle, it would be ok to add instances of Tree to our pqueue collection. This cannot be what causes the error.
Now, if we look closer to the way the Tree is created we can see a suspicious argument nl. This is interesting because of two reasons: (i) the variable nl is not defined in the part of the code we were given and (ii) the variable nl is the key that will be used by the Tree to respond to the #<= message. Therefore, nl must be the linefeed character $<10>. Which makes a lot of sense because nl is an abbreviation of newline and in the Linux world newlines are linefeeds.
Conclusion: The problem seems to be caused by the wrong argument nl used for the Tree's key.
I'm trying to make an array with random numbers (just 0 or 1), but when I run it, it just prints this: End of statement list encountered ->
This is my code:
GenList
| lista |
lista := Array new: 31.
1 to: 30 do: [ :i | lista at: i put: 2 atRandom - 1]
^lista
What can I do?
Some interesting things to consider:
1. The method selector doesn't start with a lowercase letter
It is a tradition for selectors to start with a lowercase letter. In this sense, genLista would be more correct than GenLista.
2. The method selector includes the abbreviated word 'gen'
For instance, genLista could be renamed to genereLista o listaAlAzar (if you decide to use Spanish)
3. The Array named lista has 31 elements, not 30
The result of Array new: 31 is an array of 31 elements. However, the code below it only fills 30 of them, leaving the last one uninitialized (i.e., nil). Possible solution: lista := Array new: 30.
4. A dot is missing causing a compilation error
The code
1 to: 30 do: [ :i | lista at: i put: 2 atRandom - 1]
^lista
does not compile because there is no dot indicating the separation between the two sentences. Note that the error happens at compilation time (i.e., when you save the method) because the return token ^ must start a statement (i.e., it cannot be inlined inside a statement).
There are other cases where a missing dot will not prevent the code from compiling. Instead, an error will happen at runtime. Here is a (typical) example:
1 to: 10 do: [:i | self somethingWith: i] "<- missing dot here"
self somethingElse
the missing dot will generate the runtime error self not understood by block.
5. There is a more expressive way of generating 0s and 1s at random
The calculation 2 atRandom - 1 is ok. However, it forces the reader to mentally do the math. A better way to reveal your intention would have been
#(0 1) atRandom
6. When playing with random numbers don't forget to save the seed
While it is ok to use atRandom, such a practice should only be used with "toy" code. If you are developing a system or a library, the recommended practice is to save the seed somewhere before generating any random data. This will allow you to reproduce the generation of random quantities later on for the sake of debugging or confirmation. (Note however, that this will not suffice for making your program deterministically reproducible because unordered (e.g. hashed) collections could form differently in successive executions.)
When DO is followed by a function, that function is executed and the remaining values are consumed as arguments according to the arity of the given function, e.g.,
do :multiply 3 4
multiply 3 4
These two statements are identical in their effects. But I think DO + function receives special treatment by the REBOL interpreter, because I don't believe it's possible to implement your own DO (with the exact same syntax) in pure REBOL, e.g.,
perform: func [f [any-function!]] [
; What goes here?
]
Is this correct?
Clarification
I am not asking about the DO dialect. This is not a "beginner" question. I understand REBOL's general syntax very, very well: Bindology (an old blog post I did on it), the implications of its homoiconicity, the various flavors of words, and all the rest. (For example, here is my implementation of Logo's cascade in REBOL. While I'm at it, why not plug my Vim syntax plug-in for REBOL.)
I'm asking something more subtle. I'm not sure how I can phrase it more clearly than I already have, so I'll ask you to read my original question more carefully. I want to achieve a function that, like DO, has the following capability:
do :multiply 3 4
double: func [n] [n * 2]
do :double 5
Notice how the syntax do :double or do :multiply consumes the appropriate number of REBOL values after it. This is the key to understanding what I'm asking. As far as I can tell, it is not possible to write your own REBOL function that can DO this.
You'll have answered this question when you can write your own function in pure REBOL that can be substituted for DO in the examples above—without dialects, blocks, or any other modifications—or explain why it can't be done.
The cause of the behavior you are seeing is specifically this line of code for the Rebol native DO.
/***********************************************************************
**
*/ REBNATIVE(do)
/*
***********************************************************************/
{
REBVAL *value = D_ARG(1);
switch (VAL_TYPE(value)) {
/* ... */
case REB_NATIVE:
case REB_ACTION:
case REB_COMMAND:
case REB_REBCODE:
case REB_OP:
case REB_CLOSURE:
case REB_FUNCTION:
VAL_SET_OPT(value, OPTS_REVAL); /* <-- that */
return R_ARG1;
This OPTS_REVAL can be found in sys-value.h, where you'll find some other special control bits...like the hidden "line break" flag:
// Value option flags:
enum {
OPTS_LINE = 0, // Line break occurs before this value
OPTS_LOCK, // Lock word from modification
OPTS_REVAL, // Reevaluate result value
OPTS_UNWORD, // Not a normal word
OPTS_TEMP, // Temporary flag - variety of uses
OPTS_HIDE, // Hide the word
};
So the way the DO native handles a function is to return a kind of "activated" function value. But you cannot make your own values with this flag set in user code. The only place in the entire codebase that sets the flag is this snippet in the DO native.
It looks like something that could be given the axe, as APPLY does this more cleanly and within the definitions of the system.
Yes, in Rebol 3:
>> perform: func [f [any-function!]] [return/redo :f]
>> perform :multiply 3 4
== 12
>> double: func [n] [n * 2]
>> perform :double 5
== 10
You might find it interesting to read: Why does return/redo evaluate result functions in the calling context, but block results are not evaluated?
This is a good question, and I will try to explain it to the best of my understanding.
The two statements above are identical in effect, but it is worth diving deeper into what is happening.
The :word syntax is known as a get-word! and is equivalent to writing get 'word. So another way of writing this would be
do get 'multiply 3 4
multiply is just another word! to Rebol.
The do dialect is the default dialect used by the Rebol interpreter.
If you want to implement your own version of do you need to be evaluating your code/data yourself, not using do. Here is a trivial example:
perform: func [ code [block!]] [ if equal? code [ 1 ] [ print "Hello" ] ]
This defines perform as a function which takes a block of code. The "language" or dialect it is expecting is trivial in that the syntax is just perform an action (print "hello") if the code passed is the integer 1.
If this was called as
perform [ multiply 3 4 ]
nothing would happen as code is not equal to 1.
The only way it would do something is if it was passed a block! containing 1.
>> perform [ 1 ]
Hello
Expanding on this slightly:
perform: func [ code [block!]] [ if equal? code [ multiply 3 4 ] [ 42 ] ]
would give us a perform which behaves very differently.
>> perform [ multiply 3 4 ]
== 42
You can easily write your own do to evaluate your dialect, but if you run it directly then you are already running within the do dialect so you need to call a function of some kind to bootstrap your own dialect.
This jumping between dialects is a normal way to write Rebol code, a good example of this being the parse dialect
parse [ 1 2.4 3 ] [ some number! ]
which has it's own syntax and even reuses existing do dialect words such as skip but with a different meaning.
a > b
ifTrue:[ 'greater' ]
ifFalse:[ 'less or equal' ]
My understanding is that Boolean a > b receives the message ifTrue:[ 'greater' ], and then ifFalse:[ 'less or equal' ] complying to the generalization:
objectInstance selector; selector2
But there a semicolon is needed to specify that the receiver of selector2 is not (objectInstance selector) but objectInstance. Is not the same with the above conditional execution?
The selector of the method is Boolean>>ifTrue:ifFalse:, which means it is one method with two parameters, not two methods with one parameter.
Ergo, to invoke the method, you send it the message ifTrue:ifFalse: with two block arguments.
Note that for convenience reasons, there are also methods Boolean>>ifFalse:ifTrue:, Boolean>>ifTrue: and Boolean>>ifFalse:.
Everything relevant has already been sayd, but just for your amusement:
As already told,
rcvr ifTrue:[...] ifFalse:[...]
is the one and single message #'ifTrue:ifFalse:' with 2 args sent to rcvr. The value of that expression is the one from that message send.
In contrast:
rcvr ifTrue:[...]; ifFalse:[...]
is a cascade of 2 sequential messages (#'ifTrue:' and #'ifFalse:'), each with 1 arg sent to rcvr. The value of the expression is the one returned from the last send.
Now the funny thing is that booleans do understand ifTrue: / ifFalse: (each with 1 arg),
so your code works for the side effect (evaluating those blocks), but not for its value.
This means that:
a > b ifTrue:[Transcript showCR:'gt'] ; ifFalse:[Transcript showCR:'le']
generates the same output as:
a > b ifTrue:[Transcript showCR:'gt'] ifFalse:[Transcript showCR:'le']
but:
msg := a > b ifTrue:['gt'] ; ifFalse:['le']
will generate different values in msg than:
msg := a > b ifTrue:['gt'] ifFalse:['le']
depending on the values of a and b. Try (a b)=(1 2) vs. (a b)=(2 1)...
The problem of many Smalltalk beginners is that they think of ifXXX: as syntax, where it is actually a message send which generates value. Also, the semi is not a statement separator as in many previously learned languages, but a sequencing message send construct.
A bad trap for beginners, because the code seems to work for some particular value combinations, whereas it generates funny results for others.
Let's hope your unit tests cover these ;-)
edit: to see where the bad value comes from, take a look at what is returned by the Boolean >> ifFalse: method for a true receiver...