I am interested in creating my own Stream subclass and I'm wondering what methods I should override (deploying on pharo and Gemstone). I have a collection with various types of things in it and I want to be able to stream over a subset of it, containing elements of a class. I don't want to copy the collection or use a collect: block because the collection may be large. My first use case is something like this:
stream := self mailBox streamOf: QTurnMessage.
stream size > 1
ifTrue: [ ^ stream at: 2 ]
ifFalse: [ ^ nil ]
Any pointers on what methods to override?
In Smalltalk, when we say Stream we refer to objects that respond to a basic protocol given by some few methods such as #next, #nextPut:, #contents, etc. So, before going into further detail I would say that stream at: 2, as you put in your example, is not a very appropriate expression. More appropriate expressions for a Stream would be
stream position: 2.
^stream next
So, the first thing you have to consider is whether you are looking for a Stream or a Collection. This basic decision depends on the behavior your objects will have to implement.
Use a subclass of Stream in case you decide that you want to enumerate the elements using #next, i.e. mostly in sequential order. However, if you want to access your elements via at: index, model your objects with a subclass of SequenceableCollection.
In case you choose streams, you will have to decide whether you will be only accessing them for reading operations or will also want to modify their contents. Your description of the problem seems to indicate that you will just read them. Therefore the basic protocol you have to implement first is
#next "retrieve the object at the following position and advance the position"
#atEnd "answer with true if there are no more objects left"
#position "answer the current position of the implicit index"
#position: "change the implicit index to a new value"
Also, if your streams will be read only, make your class a subclass of ReadStream.
There are some few other additional messages you will have to implement if you want to inherit fancier methods. An example would be #next: which retrives a subcollection of several consecutive elements (its size being given by the argument.)
If you instead think that it would be better to model your objects as collections, then the basic protocol you will have to implement consists of the following three methods
#at: index "retrieve the element at the given index"
#size "retrieve the total number of elements"
#do: aBlock "evaluate aBlock for every element of the receiver"
(I don't think your collections have to support at:put:.)
Very recently we had the same problem you are describing and decided to model our objects as collections (rather than streams.) However, regardless of which approach you will finally follow I think that you should try both and see which one is better. Nobody will give you better advice than your Smalltalk system.
Incidentally, note also that if you have a (sequenceable) Collection, you will get a Stream for free: just send #readStream to your collection!
I needed to override next and atEnd. My Stream subclass takes a block and a collection, and iterates over all elements of the collection for which block evaluates to true.
Example usage:
e := Array with: 1 with: 2 with: 3.
a := QStream collection: e block: [ :i| i odd ].
a next. "1"
a next. "3"
Here is the core of it:
Stream subclass: #QStream
instanceVariableNames: 'collection block index found'
classVariableNames: ''
poolDictionaries: ''
category: 'QDialog-Core'
initialize
super initialize.
index := 1.
self search.
next
| result |
result := found.
self search.
^result.
search
[ index < collection size and: [ (block value: (collection at: index)) not ] ]
whileTrue: [ index := index + 1 ].
self atEnd
ifTrue: [ ^ found := nil ]
ifFalse: [
found := collection at: index.
index := index + 1 ]
atEnd
^ index > collection size
Related
it seems very difficult to get a proper source of information about Squeak.
I have a few basic questions about it:
does '=' check for equality by refrence?
does '==' check for equality of values?
collection - linked list - if I do something like:
list := LinkedList new.
element := list first.
does it mean that element and 'list first' are both references to the same place in memory (the first place in thr linked list?)
why do I need to override the operator = for linked list? and how do I do it?
By default == is equality by reference. In Object = is defined as
= anObject
^ self == anObject
But other classes usually override it. For example in Character = is defined as
= aCharacter
^ self == aCharacter or:[
aCharacter isCharacter and: [
self asciiValue = aCharacter asciiValue]]
You can get all implementors of = by executing #= implementors.
In your case element and list first are referencing the same object. This is because first is implemented as
first
^ self at: 1
And at returns the element on position 1. But if first would be implemented as
first
^ (self at: 1) copy
then it would return a copy of an element (or if you use element := list first copy) and then they will return false when compared with ==, but if = is implemented in a smart way it should return true in most cases.
Also be sure that you want to use LinkedList because in pharo which is a fork of squeak it is used mostly for process scheduling and I think that there was a discussion that LinkedList is more of a utility collection. The most used collection with random access features is OrderedCollection
I'm reading a book on Smalltalk and I have an exercise about the anomaly of disappearing element I'm not able to solve.
Object subclass: Book [
| isbn |
<comment: 'A book class'>
setIsbn: anIsbn [
isbn := anIsbn.
]
getIsbn [
^isbn.
]
= anotherBook [
^self getIsbn = anotherBook getIsbn.
]
]
| Library |
Library := Set new: 100.
Library add: (Book new setIsbn: '0-671-2-158-1').
(Library includes: (Book new setIsbn: '0-671-2-158-1')) printNl.
I read I have to override the hash method too, but I don't know how to do it. How do I amend the Book class in order to avoid the anomaly?
I can't really tell what are you asking about, but to override hash, you should do the same as with =, which you have overridden as well, just by including different definition. So you do something like:
hash [
"return hash here"
]
If you are asking what hash should be like… well think about it like that: objects that are equal have to have the same hash (but this doesn't have to work the other way around). So I'd suggest you to do something like
hash [
^ self getIsbn hash
]
Also about disappearing element. Set is a hashed collection. This means that before comparing it's element with the one you are looking for, it select's a subset by hash. So if you are not overriding hash it may select a subset that won't contain your desired element.
In the end I'd suggest you to use some different implementation of Smalltalk, because my head hurt when I was starting to learn it with gnu-smalltalk. Personaly I use pharo it provides a nice UI and allows you to see what you override, allows you to debug, etc.
There are a few further Smalltalk idiomatic issues with your code:
normally accessors in Smalltalk don't use get and set. So you'd have isbn: anIsbn and isbn.
you probably want to create an extra constructor that has the ISBN as a parameter so you don't have to send both new and the setter yourself:
Book>>onIsbn: anIsbn
^self new
isbn: anIsbn;
yourself
The basic rule is never override #= without overriding #hash
Let us say I have a situation like this:
;; Capture whatever the print word pointed to into a variable
outer-print: :print
foo: context [
;; within foo, override print and implement in terms of outer-print
print: func [value] [
outer-print "About to print"
outer-print value
outer-print "Done printing"
]
]
I can do this, or if I have more than one thing I want from the outer context I could capture it explicitly:
;; Capture current context into something called outer
outer: self
foo: context [
;; within foo, override print and implement in terms of outer/print
print: func [value] [
outer/print "About to print"
outer/print value
outer/print "Done printing"
]
]
Is this the right idiom, or is there a better way of doing it? Are there circumstances where this might not give me what I expect?
this is good style, especially the second, which is more flexible as it allows you to mass-effect all uses of the outer print, without any ambiguity. when using direct binding, it may occur that the word outer-print is redefined or the context changes between two calls to make foo [] and in the end, points to two different bindings.
static symbol resolving
For the sake of completeness there is a third alternative which doesn't require any extra words to be setup. I don't have a proper naming for it, feel free to suggest a better title.
This method defies any binding issues down the line because you use the function value directly:
foo: context compose/deep [
;; within foo, override print and implement using native print directly
print: func [value] [
(:print) "About to print"
(:print) value
(:print) "Done printing"
]
]
Now the interesting part is if you SOURCE the inner print function:
>> p: get in foo 'print
>> SOURCE P
== p: func [value][native "About to print" native value native "Done printing"]
see how the native value of print is used directly in the body, instead of a word referring to it.
This is, in fact, probably the closest we can get to some form of compilation in pure REBOL. instead of constantly using symbols to fetch and evaluate, we can simply statically resolve them manually, using reduce or compose as in the above.
pros:
It can never be hi-jacked by some advanced and malicious binding code, i.e. even if there are no direct word bounds to PRINT in ANY and ALL contexts, you still have a direct reference to the original function in your body.
cons:
Its a very static way to code, and isn't very "Rebolish".
The
;; Capture current context into something called outer
comment suggests that you think there is some "current context" in Rebol. That is false. Every word has got its own context. Thus, there are cases when your
outer: self
code doesn't work as you expect. For example, let's suppose that you want to access two variables, 'print and 'set. It is possible for the words to have different "outer" contexts. In that case the trick will be certain to not work for at least one of the words, but it may, in fact, not work for both.
Any idea why the following doesn't work? (R3)
o: make object! [
foo: does [do "bar"]
bar: does [print "hello from bar"]
]
o/foo
** Script error: bar has no value
** Where: catch either -apply- do foo
** Near: catch/quit either var [[do/next data var]] [data]
Try this:
o: make object! [
foo: does [do get bind load "bar" self]
bar: does [print "hello from bar"]
]
o/foo ;this will work
You need that BINDing because your "bar" lives in the object, not in global scope.
Check this out as well:
my-func: does [print "ok"]
o: make object! [
foo: does [do "my-func"]
bar: does [print "hello from bar"]
]
o/foo ;this will work too
You need to LOAD it because it is a string, but it has to be a word to be BINDed.
So these will work too (put them in your object):
do get bind to-word "bar" self
or
do get bind 'bar self
No Scope!!!?
The reason do "self/bar" cannot know where to find 'BAR is because there is no scope in Rebol (not in the traditional CS meaning at least).
Words in Rebol only have meaning once they have been statically bound to a context. This automagically occurs when you 'MAKE an object, so many people don't even realize it even after years of use.
Here are the steps (loosely) when an object (a.k.a. context) is created.
It picks up all the root set words in its spec (in this case [FOO: BAR:] )
Adds them to its current internal words (SELF: by default, more if you are using an object as basis)
Then binds all the words in the block (hierarchicaly) to those set-words it added to its spec.
Executes the block.
So you see, once you execute the block its too late, the words already got assigned their meaning, which allows the interpreter to ask for their values (which could trigger an expression evaluation, hence the E in REBOL).
Global, cause that all there really is once executing.
DO and LOAD cannot automatically bind to anything but the global context... because there is no such thing as the "current context" like you'd have in traditional OOP and imperative languages (remember, no scope). Really, once its executing, that information doesn't exist anymore, unless you bound the "current" context to a word... which is what 'SELF does, but to be bound it has to already be loaded, which, when executing a string, never occured.
clueless functions
I'll finish by adding that it may not be obvious at first sight, but while it was binding the Object spec block, it still didn't know what FOO and BAR really were. in fact, the only way FOO and BAR could access the 'O object, is because their function block, when it was run thru 'MAKE, got bound to the object... yep, before it even knew it was a function. then if the function defined its own locals, it would re-bind its body block to those new locals.. because you guessed it... a function creates its own inner context, which gets the same MAKE treatment (but without the internal SELF word).
I hope this helps clear things in a more obvious light.
here is a proof that code isn't scoped:
a: make object! [
data: "HAHAHAAAAA!!!"
action: does [print self/data]
]
b: make object! [
data: "BUMBLING BEHEMOT"
action: does [print self/data]
]
b/action: get in a 'action
; this will print HAHAHAAAAA!!!
b/action
To explain moliad's answer a bit more, see the following explanation:
REBOL words carry a reference to their context with them. It’s not
where a word is evaluated that makes the difference, but where it’s
declared.
from http://blog.revolucent.net/2009/07/deep-rebol-bindology.html
Here is a very good example:
x: 0
b: [] loop 3 [use [x] [x: random 100 append b 'x]]
;== [x x x] <-- there are three X which looks same words (and they are same actually)
reduce b
;== [95 52 80] <-- but they have different values in their contexts
probe x
;== 0 <-- in global context, it has another value as well
This looks weird at first look, but actually it is not. USE creates a new context each time in the LOOP, we set X (in the context the USE created) to a value, then APPEND the WORD (not the value!) to a block.
All the words that we append to the block carries their own contexts with them, but they look same word.
When we REDUCE (or PRINT etc.), when we GET their values in their own contexts, we see that they all have different values!
I'm new to smalltalk and I'm impressed with the fact that there are only just 6 keywords in the language (self, super, true, false, nil & thisContext), and how pure it is in having almost everything as message passing, eg. looping using whileTrue, if/else using ifTrue, etc ... which are way different from what I'm used to in other languages.
Yet, there are cases where I just cannot make sense of how message passing really fit in, these include:
the assignment operator :=
the cascading operator ;
the period operator .
the way to create a set #( ... )
These aren't message passing, right?
As you've discovered, there's still some actual Smalltalk syntax. Block construction, literal strings/symbols/comments, local variable declaration (|...|), and returning (^) are a few things you didn't mention which are also syntax.
Some extensions (e.g. #(...), which typically creates an Array, not a set) are certainly expressible otherwise, for example #(1 2 3) is equivalent to Array with: 1 with: 2 with: 3; they're just there to make the code easier to read and write.
One thing that might help clarify : self, super, true, false, nil & thisContext are data primitives, rather than keywords.
They are the only 6 data primitives. These 6 are also known as pseudo-variables. Absolutely every other thing is an instance of Class Object or its subclasses.
There are very few pre-defined keywords in Smalltalk. They can be written in a very condensed form.
A famous example is Smalltalk Syntax on a Postcard (link)
exampleWithNumber: x
| y |
true & false not & (nil isNil) ifFalse: [self halt].
y := self size + super size.
#($a #a "a" 1 1.0)
do: [ :each |
Transcript show: (each class name);
show: ' '].
^x < y
Here's the comment for this method - which is larger than the method itself:
"A method that illustrates every part of Smalltalk method syntax
except primitives. It has unary, binary, and keyboard messages,
declares arguments and temporaries, accesses a global variable
(but not an instance variable), uses literals (array, character,
symbol, string, integer, float), uses the pseudo variables
true, false, nil, self, and super, and has sequence, assignment,
return and cascade. It has both zero argument and one argument blocks."