How to override equality method in Smalltalk? - smalltalk

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

Related

Documenting Rebol's dialect

I can document a function like this:
f: func [
"a description"
arg1 [string!] "a description of an argument 1"
][
arg1
]
I can use ?/help in order to retrieve informations about the the function (a description, a usage, the argument list, a description of each argument and it's type)
? f
USAGE:
F arg1
DESCRIPTION:
a description
F is a function value.
ARGUMENTS:
arg1 -- a description of an argument 1 (Type: string)
I cannot document dialects like this. Is there an automatic way to document dialects (like func does)? Do I have to do this manually?
There's nothing for it currently, but it's a good idea. So good that someone has suggested it before. :-)
Do I have to do this manually?
You can manually write a new generator which defines your "dialect spec" format. Then either do something like give it a HELP command, or extend HELP to recognize it.
Very short example to demonstrate a group of techniques which may come in handy in doing something like this (not all expected to be obvious, rather to hint at the flexibility):
make-dialect: function [spec [block!] body [block!]] [
return function ['arg [block! word!]] compose/deep/only [
case [
arg = 'HELP [
foreach keyword (spec/keywords) [
print [keyword "-" {your help here}]
]
]
block? arg [
do func [arg] (body) arg
]
'default [
print "Unrecognized command. Try HELP."
]
]
]
]
So there's your function that takes a dialect spec and makes a function. Once you've got your generator, using it can be less manual:
mumble: make-dialect [keywords: [foo baz bar]] [
print ["arg is" mold arg]
]
>> mumble help
foo - your help here
baz - your help here
bar - your help here
>> mumble [<some> [dialect] {stuff}]
arg is [<some> [dialect] {stuff}]
The techniques used here are:
Soft Quoting - Usually you would have to say mumble 'help to "quote" the help as a lit-word! to get it to pass the word! to mumble (as opposed to running the default HELP command). But because arg was declared in the generated function as 'arg it was "soft quoted"...this means that words and paths will not be evaluated. (Parens, get-words, and get-paths still will be.) It's a tradeoff because it means that if someone has a variable they want to pass you they have to say :var or (var) as the argument instead of just var (imagine if the block to pass the dialect is in a variable) so you don't necessarily want to use it...but I thought it an interesting demo to make mumble help work without the lit-word!
Deep Composition - The spec and the body variables which are passed to make-dialect only exist as long as make-dialect is running. Once it's over, they'll be gone. So you can't leave those words in the body of the function you are generating. This uses COMPOSE/DEEP to evaluate parens in the body before the function generator runs to make the result, effectively extracting the data for the blocks and stitching them into the function's body structure.
Reusing Function's Binding Work - The generated function has a spec with a parameter arg that didn't exist at the call site of make-dialect. So arg has to be rebound to something, but what? It's possible to do it manually, but one easy way is to let FUNC do the work for you.
Those are some of the techniques that would be used in the proposed solution, which seeks to not only document dialects but provide an easy method by which their keywords might be remapped (e.g. if one's Rebol system has been configured for another spoken language).

Subclassing Stream

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

How to capture words bound in outer context when creating new context?

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.

Type declarations on REBOL objects

I know you can type declare arguments and returns on functions
some-func: function [
"some func"
number [ integer! ]
] [
result [ integer! ]
] [
help number
return number
]
some-func 1
some-func "blah"
NUMBER is an integer of value: 1
** Script error: some-func does not allow string! for its number argument
How about object properties though?
o: make object! [
a [string!]
b [integer!]
c [o2]
none
]
o2: make object! [
c [string!]
]
an-object: make o [
a: 3.141
b: "an integer"
c: "blah"
]
help an-object
N-OBJECT is an object of value:
a decimal! 3.141
b string! "an integer"
c string! "blah"
I've seen the type declaration on properties as examples, but is it just for documentation?
This is a really good question, and something I've thought about for years. It turns out that Rebol's internal object storage mechanism can handle this, but there's no way of expressing it in source code. Why not you ask? Here's why:
Rebol currently has the concept of name-value pairs. That's how contexts and objects are expressed. However, it is often desirable for objects to include other information that's not just a name or value. The datatype is a good example. Other examples are comments attached to values, and protections/permissions on values (such as allowing read and write).
So, the problem becomes: how many various features do we want to support in the language syntax, and specifically how would we do that? It gets further complicated by the "optional" characteristic of these features. So, you can't really use positional semantics to describe the object. That means adding a syntactic method, which means adding keywords (because Rebol really tries to avoid punctuation.)
So, as a result, the source form would become fairly verbose, and I think we could question whether it would be worth the benefit we'd get from allowing the feature in the first place.
So, this is a case where the simple principle of Rebol takes precedence over feature creep.
All that said, if you've got an idea for a simple method of doing it, let it be known!
It's just for documentation .. type checking is only done on functions.

How do I change a method's name dynamically in squeak?

I have a class and I want to change the name of a specific method in run time.
I guess there's a method in the 'Behavior' class that does it. But I just can't find it. any help? [in squeak]
The normal way a user does this is to modify the method source and 'accept it' then delete the old version. So it's not likely that basic Squeak includes a single method to do this, although I could be wrong.
However if you install, for example, OmniBrowser there is a method refactoring called 'rename' and you could inspect and find code to perform this refactoring. It is fairly complex, firstly because the refactorings are done using the command pattern which involves a little redirection to work out, but secondly because this is a fairly complex refactoring which includes modifying the call sites.
What you are suggesting puts HUGE red flags up for me.
What is it you are trying to accomplish with this?
Do you mean you want to change the name of the method you are calling at runtime?
If so, that's easy.
do something like:
|methodName|
methodName := self useMethod1 ifTrue: [#method1 ] ifFalse:[ #method2 ].
self perform: methodName.
You best use a refactoring
r := RenameMethodRefactoring
renameMethod: #foo:foo:
in: Foo
to: #bar:bar:
permutation: (1 to: #foo:foo: numArgs).
r execute.
Avoid voodoo magic in real code when possible.
That being said you can do some very interesting things by manipulating methods dynamically.
For instance the code bricks in Etoys are translated into Smalltalk methods. Other DSL implementations can also benefit from similar metaprogramming tricks.
After experimenting a bit I came up with the following code for renaming unary methods:
renameMethod: oldMethod inClass: class to: newMethod
| oldSelector newSelector source parser |
oldSelector := oldMethod asSymbol.
newSelector := newMethod asSymbol.
oldSelector = newSelector ifTrue: [^self].
"Get method category"
category := (LocatedMethod location: class selector: oldSelector) category.
"Get method source code"
source := class sourceCodeAt: oldSelector.
"Replace selector in method source"
(parser := class parserClass new) parseSelector: source.
source := (newSelector asString), (source allButFirst: parser endOfLastToken).
"Compile modified source"
class compile: source classified: category.
"Remove old selector"
class removeSelector: oldSelector
You could probably find an easier way to do this if you browse through the Squeak code a bit longer than I did.
You can't change a method's name, really, because it doesn't have one.
An object's method dictionary maps Symbols to CompiledMethods. "Change the name of a method" means "move the CompiledMethod value from this key to that key".