How to change the instance variable of a class in Pharo? - smalltalk

I created a class named Die (singular of Dice) and set an instance variable side.
Object subclass: #Die
instanceVariableNames: 'side'
classVariableNames: ''
package: 'MyPackage'
I initialized the side variable to 6 by default:
initialize
side := 6.
However I would like the ability to change the instance variable after the object has been created, like so:
myDie := Die new.
myDie side: 10. " it doesn't to work "
It seems to me that instance variables can only be changed by class methods. Is there a way to change it like above?
Also, I have a method roll that returns a random number between 1 and the specified side. If I create two dice and want to roll two dice at the same time, or maybe three dice at the same time, should I create a different class, something like papercup feed that with the number of dices and implement the roll method on the papercup instead?
Thank you.

You are on a right track. When you execute
myDie side: 10.
You send a message side: to myDie which is an instance of a class Die. To "respond" to this message, the class die (or its superclasses) should implement a side: method. Ideally, this method is going to look like this:
side: aNumber
side := aNumber
You see, pharo takes encapsulation very seriously, so instance variables can be directly accessed only by the object itself, and not by anyone from the outside. This is why you always have to create accessors, which is a good practice followed to a various extent by many programming languages.
Regarding your second question: it's a good idea to have a Papercup class, but if you don't need extensive features, the exact task can be achieved by the following code:
papercup := OrderedCollection new.
papercup
add: die1;
add: die2;
add: die3.
papercup collect: #roll
This way you will roll all the 3 dies and return a collection for 3 numbers (collect: is the same as map() in many programming languages). I provided a shorthand version, whereas the full way to write a collect is: papercup collect: [ :die | die roll] when you specify a complete block (closure) and send the roll message to each die.

Related

squeak(smalltalk) how to get all the methods of an object (inherited methods too)

how can I get a list of all the methods an object can understand?
for example:
set := 8 getAllMethods
will give me all methods 8 can understand in set
In code you can use allSelectors:
set := 8 class allSelectors
gives you a set of all the names of messages (a. k. a. selectors) that 8 can understand.
If you need the CompiledMethods instead of only the message names, you can use lookupSelector:
| class |
class := 8 class. "will be SmallInteger"
set := class allSelectors collect: [:each | class lookupSelector: each]
If you don't want to do this in code but rather find out in the IDE which messages an object can understand, then I propose to use the protocols browser (a. k. a. Lexicon tool). You can open it via "browse protocol" from the context menu of a class:
I used it to find allSelectors and lookupSelector:, which are inherited from Behavior and not defined in Class itself.
This is interesting because of the following. At first glance one might be tempted to consider an expression like this one
anObject class withAllSuperclasses gather: [:class | class methodDictionary]
which gathers all the methods implemented in the class and its superclasses. However, if a method is defined in a class and in one of its superclasses, we should ignore the latter because anObject will use the one in the class.
To remedy this side-effect of the above script we need to gather only the methods that are defined in the class which is closer to anObject class. One way to do this is to enumerate the classes from top to bottom, adding all their methods to a Dictionary. Since the dictionary will only retain the last element added to a given key (in this case a selector), only the ones that belong in the protocol of anObject will survive:
methods := Dictionary new.
anObject class withAllSuperclasses reverseDo: [:class |
methods addAll: class methodDictionary associations].
Note the use of reverseDo: for enumerating the classes downwards.
Another approach would be to enumerate the classes from bottom to top, checking whether the selector has already been visited:
methods := Dictionary new.
anObject class withAllSuperclasses do: [:class |
class methodDictionary do: [:cm |
methods at: cm selector ifAbsentPut: [cm]]]
(were cm stands for CompiledMethod)
The second version is a bit longer, more complex (it has two loops, one nested inside the other) and needs conditional logic (#at:ifAbsentPut:). In other words, it shouldn't be the one chosen.
Note
When looking for ways to create a collection (in this case the collection of all methods understood by an object), first make sure you really need such a collection. For instance, you will need the collection if you want to display it on the screen. However, if you are only going to use the collection for membership checking, there might be other ways to proceed. In your case, you could simply ask the object:
anObject respondsTo: <selector>
and in case the answer is true, recover the method using
anObject class lookupSelector: <selector>.
This is both simpler and more efficient because it doesn't create collections, etc.

Changing type of class field in inherited class

I am using newest Delphi 10.3.3
I have several main classes, which are extended from same parent class and also some, let's call it, Reflection classes, which has also same parent. I want instance of main class to have link to corresponding reflection instance, but I run to this issue (I simplified it to this example):
Main classes:
TMainClass = class
Link: TReflectionClass;
end;
TCarMainClass = class(TMainClass)
Link: TCarReflectionClass;
end;
Reflection classes:
TReflectionClass = class;
TCarReflectionClass = class(TReflectionClass);
The problem lies in Link field. I want Link field in TCarMainClass to be directly defined as TCarReflectionClass to avoid type-casting on lot of pieces of code (and also risking some errors), however if I define class like this, TCarMainClass.Link just hides TMainClass.Link - it is defined as different field with same name. That is not good because it consumes extra memory and mainly I can't access that field from parent class, which I want (as pointer to generic instance).
Of course I can solve this by making field private of common type and define property setter/getter which handles re-typing in each class. But that is lot of extra code for these classes and also overhead on each get/set because of calling getter/setter methods.
My question is - is there some easy hack I missed, to tell compiler I want field in child class to occupy same memory location as certain field in parent class?
Thanks
and mainly I can't access that field from parent class
While it's true that parent class can't access the descendant's field, there is nothing preventing you from synchronizing the 2.
procedure TCarMainClass.SetLink(const Value : TCarReflectionClass);
begin
FLink := Value;
TMainClass(Self).FLink := Value;
end;
Now, if you absolutely require no additionnal memory use and no setters, the only remaining option (that I can think of at this time) is as GolezTrol suggested, generics.
TMainClass<T : TReflectionClass> = class
Link: T;
end;
TCarMainClass = class(TMainClass<TCarReflectionClass>)
end;
But that would probably break your design though, because TCarMainClass would be incompatible with TMainClass<TReflectionClass>. (if you want to understand why, search for the terms covariance/contravariance).

Smalltallk - How can I get an Array (or Collection) of the all the Instance variables in an Object (the current Instance) of a Class?

Let's say we have a Class and we instantiate it, creating an Instance of that Class. This Instance has a number of (instance)variables, defined by the class, that I need to use. I'd like to get all these (instance)variables in an Array or some Collection so I can iterate through them and set them to some value, not nil.
How can I do this?
I would like to build up on #Uko's answer because there is a more direct way to implement his idea.
The message instSize sent to a Class will answer the number of named instance variables of its instances. This, of course, would include instance variables defined in superclasses.
For instance, RemoteTempVectorNode instSize answers with 17 (wow!). Therefore you could do:
fields := (1 to: anObject class instSize) collect: [:i | anObject instVarAt: i]
and then, change them with:
values withIndexDo: [:v :i | anObject instVarAt: i put: v]
where values is the array of new values you want to inject into the object.
So, why I'm suggesting this instead of instVarNamed:? Because the latter is indirect. If you take a look at its implementation you will see that it has to first find out the name of the i-th ivar by sending instVarIndexFor:ifAbsent: to the object's class. In other words, if you need the ivar names, follow #Uko's suggestion; otherwise don't bring them into the equation because they will only add CPU cycles to your program.
One more thing. As #Sean DeNegris wisely raised in his comment to your question, it would be beneficial if you elaborated a little bit more on why you need such an unusual maneuver.
EDIT:
Now that Pharo has Flexible Object Layouts the mapping between inst var names and the class instSize is no longer valid (in classes that use the new capability.) So, the simpler approach of using just indexes would not work with generality. In fact, under the new "taxonomy" the instSize (number of fields) of an object may be different from the #numberOfInstanceVariables. I guess that the added flexibility has its costs and benefits.
You can send #allInstVarNames to a class (Behavior) to get names of all instance variables defined by it and by superclasses. If you need without superclass variables, you can use #instVarNames
Let's say that var is your variable that you need to work with. Then you can get the collection of instance variable names and iterate them.
You can use #instVarNamed:put: to set instance variable by name, and #instVarNamed: to get the value by name (in case you need).
I think that something like this may help you:
var class allInstVarNames do: [ :instVarName |
var instVarNamed: instVarName put: <yourValue>

Smalltalk initialization variables

In languages like Java and C++ we give parameters to constructors.
How do you do this in Pharo Smalltalk?
I want something like
|aColor|
aColor = Color new 'red'.
Or is this bad practice and should I always do
|aColor|
aColor = Color new.
aColor name:= red.d
The short answer is that you can do pretty much the same in Smalltalk. From the calling code it would look like:
aColor := Color named: 'Red'.
The long answer is that in Smalltalk you don't have constructors, at least not in the sense that you have a special message named after the class. What you do in Smalltalk is defining class-side messages (i.e. messages understood by the class, not the instance[*]) where you can instantiate and configure your instances. Assuming that your Color class has a name instance variable and a setter for it, the #named: method would be implemented like:
(class) Color>>named: aName
| color |
color := self new.
color name: aName.
^color.
Some things to note:
We are using the #new message sent to the class to create a new instance. You can think of the #new message as the primitive way for creating objects (hint: you can browse the implementors of the #new message to see how it is implemented).
We can define as many class methods as we want to create new 'configured' instances (e.g. Color fromHexa:) or return pre-created ones (e.g. Color blue).
You can still create an uninitialized instance by doing Color new. If you want to forbid that behavior then you must override the #new class message.
There are many good books that you can read about Smalltalk basics at Stef's Free Online Smalltalk Books
[*] This is quite natural due to the orthogonal nature on Smalltalk, since everything (including classes) is an object. If you are interested check Chapter 13 of Pharo by Example or any other reference to classes and metaclasses in Smalltalk.
HTH
In Smalltalk all member fields are strictly private and to assign to them you'll have to define assigning methods.
Color >> name: aString
name := aString
Then you could create your object like this:
|aColor|
aColor := (Color new)
name: 'red';
yourself.
Commonly to reduce verbosity factory method is used:
Color class >> withName: aName
^ (self new)
name: aName;
yourself.
With this you could create new objects like this:
|aColor|
aColor := Color withName: 'red'.

MATLAB - obtain the object a property belongs to?

Suppose I have a myClass < handle with property A. If I create an instance of myClass, say myObj, and pass myObj.A to a function, say function myFunc(val), is it possible within myFunc to see that the val passed to it is a property of myObj?
EDIT: For context:
I'm writing an API (in a sense) to interface with Arduino hardware for my research lab. The overarching class is called Vehicle, with properties PinManager < handle, TelemCollector < handle, and various Device < handles. It also has methods to do things like runMotor(), getAltitude(), etc. I have a method TelemCollector.telemFetch() which is the callback for a timer event; I would like TelemCollector.telemFetch() to be able to access Vehicle methods (namely getAltitude()); naively I would just make Vehicle a property of TelemCollector to access those methods. I was hoping to not have to do this.
EDIT2: Sample code snippet of what I'm trying to accomplish:
classdef Vehicle < handle
properties
PinManager
TelemCollector
Devices
end
methods
function obj = Vehicle(PM, TC, D)
obj.TC = TelemCollector();
obj.PM = PinManager();
obj.Devices = D();
end
function val = getAltitude(obj)
%# read altitude from a connected Device
end
function val = getSpeed(obj)
%# read speed from connected Device
end
end
end
classdef TelemCollector < handle
properties
%# ...
end
methods
function fetchTelem(obj)
%# do getAltitude(), getSpeed(), etc, here.. but I want to access
%# Vehicle.getAltitude() and Vehicle.getSpeed() somehow!
end
end
end
For all I know, no.
For example if myObj.A is a double, myFunc will just be passed the value it contains and there will be no reference to the object. If you were calling myFunc(somevariable) where somevariable was really the name of a variable and not an expression, then calling inputname(1) inside of myFunc would give you the string 'somevariable', but since you are referring to a property of a class, this is too complicated for MATLAB and inputname(1) just returns '' (tested with MATLAB R2011a).
Update: Why do you need to know this anyhow? If your interfaces are cleanly designed, you should probably not have to do this kind of thing. Or are you trying to work around someone else's bug/bad design? Depending on your application you could think of some kind of very dirty hack involving dbstack, trying to find out which m-file called your function, read the appropriate line of code from the .m file, parse it and then access the object using evalin('caller',...) ... but I doubt that's a good idea ;-).
Edit in response to context you provided:
Can't you just redefine your Timer callback to hand over the "Vehicle" object as well? i.e.
set(yourtimer_handle,'TimerFcn',{#fetchTelem,vehicle_handle});
means that whenever the callback timer calls the function TelemCollector.fetchTelem(), it hands over vehicle_handle as a third argument as described in the docu. This works in conjunction with a changed function head
function fetchTelem(obj, event, vehicle_handle)
where you can replace event by ~ in newer MATLAB versions if you don't need it.
Could that work?
fetchTelem can't call methods of an object that it doesn't have a reference to. So, regardless, you need to provide your TelemCollector object with the Vehicle handle.
Personally, I think the association between Vehicle and TelemCollector should be in the opposite direction. I would prefer something that looked more like:
V = Vehicle(PM, D);
TC = TelemCollector(V);
Although it really depends on how you expect to use the classes.
I agree with #Jonas Heidelberg: if it's this difficult, then it's probably the wrong interface.