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.
Related
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.)
why would the following code:
| list types |
list := Heap new.
types := #('a' 'b' 'c').
types do:[ :t |
1 to:9 do:[ :i |
list add:(t, i asString).
].
].
^ list
issue the String concatenation instead of streams warning in a method in Pharo?
Clicking the [?] button shows:
String concatenation instead of streams
Check for code using string concatenation inside some iteration message.
Am I doing something that can be done easier with streams? What I want to achieve is to create a list of all values a1 to a9, b1 to b9 and c1 to c9.
It complains because of the part t, i asString that is inside a collection loop (you can look at the actual implementation of the rule in the class RBStringConcatenationRule.
Normally string concatenation is discouraged because it's slower and more memory intense (IIRC about the memory).
So if you are doing some heavy concatenation (connecting a lots of parts into a single string), stream is preferable: you can look at most printOn: methods in the system to see it in action.
However in trivial cases concatenation with , is just fine, the warning rule is just too broad. Warnings are just that... warnings that something might be wrong, or that something might be written better.
Speaking of better writing, in Smalltalk it is preferable to use specialized collection methods (select:,collect:,...) over the overly-generic do:, e.g.
| list types |
types := #('a' 'b' 'c').
list := types flatCollect: [ :t | (1 to: 9) collect: [ :i | t , i asString ].
^ Heap withAll: list
(and if you don't need Heap you can just return the third line directly and not have the list tempvar.
To mininize the number of created objects, you could do like this:
| list types digits |
list := Heap new.
types := #($a $b $c).
digits := (1 to: 9) collect: #asCharacterDigit.
types do: [ :t |
digits do: [ :d |
list
add: ((String new: 2)
at: 1 put: t;
at: 2 put: d;
yourself)
] ].
^ list
So you are not creating interim strings and intervals. Nor conversions from integer to string.
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.
Is it possible to override the + operator in smalltalk to accept two params? i.e., I need to also pass in the units for my custom class. Something like:
Number subclass: #NumberWithUnits
instanceVariableNames: 'myName unitTracker'
classVariableNames: ''
poolDictionaries: ''
category: 'hw3'
+ aNumber theUnits
unitTracker adjustUnits: theUnits.
^super + aNumber
Or is there an easier way to do this that I haven't considered?
Additional problem description:
(NumberWithUnits value: 3 unit: #seconds) should give you a NumberWithUnits that represents 3 seconds. But you should also be able to write 3 sec and that should evaluate to a NumberWithUnits (seconds is already taken in Pharo 2.0). The way to do this is to add a sec method to Number, which basically returns (NumberWithUnits value: self unit: #seconds). You can add methods for meters and elephants as well. Then you could write an expression 3 elephants / (1 sec sec) and it would return the right thing. Write a test for it to be sure!
What you're missing is the order of evaluation/precedence in Smalltalk. It's actually quite a bit simpler than in most other languages:
explicit parentheses ()
unary
binary
keyword
assignment :=
So, you can implement a unary method on Number which gets evaluated before the binary +. A simple example is Number>>negated, which is Smalltalk's version of the unary minus.
At least in Squeak/Pharo (all I've got handy at the moment), date arithmetic is already implemented similarly. Look at Number>>minutes, for example, so you can evaluate things like 5 hours - 3 minutes, which returns a Duration of 0:04:57:00.
I think a more idiomatic way to do this would be to construct a second NumberWithUnits, and then add that.
Then inside your + method you need to reconcile the units of the two things being added, then add their magnitudes.
So something like
a := Measure new: 2 #m
b := Measure new: 10 #mm
a + b
Measure class [
+ other [
"TODO: check/convert units here"
resultMagnitude := (a magnitude) + (b magnitude).
combinedUnits := (a units) * (b units).
^Measure new resultMagnitude units: combinedUnits.
]
]
See also for example the GNU Smalltalk example of operator overloading.