Pharo initialize an object with an OrderedCollection - smalltalk

Indeed it is within the framework of a research. I'm trying to generate random values ​​for the instance variables of an X object. So when instantiating the object, I replace the parameters of the object initialization method with the values ​​that are generated.
So, I manage to retrieve instance variables, generate random values. The problem now is how to replace the arguments of the initialization method with the generated values?
we have my Person class
Object subclass: #Person
instanceVariableNames: 'name lastName birthDate'
classVariableNames: ''
package: 'Moi'
the method that i use to initialize a person is:
withName: aName andBirthDate: aBirthDate andLastName: aLastName
name:= aName.
lastName:= aLastName.
birthDate:= aBirthDate
For example I have the method
Person>>#withName:aName andBirthDate:aBirthDate andLastName:aLasatName
and I have the following values:
"toto", "13 sept 2022" and "tata"
How to override or rebuild method like this
Person>>#withName:'toto' andBirthDate:'13 Sep 2022'andLastName:'tata'?
```bri
the Person class is an example. Instead of the Person class, we can have Point, Car or any other class.

No, you will need to call the constructor and break out the arguments:
Person
withName: (list at: 1)
lastName: (list at: 2)
birthDay: (list at: 3)

Related

Pharo get all setter from a class

I am new in pharo. I have a problem. I want to get all setter from a class in this language. But i don't any idea for do that.
For example, we have ClassA that have a method like:
Object SubClass: #ClassA
instanceVariableNames: 'name age'
classVariableNames: ''
package: 'MyPackage'
name:aName
name:= aName
age:anAge
age:= anAge
and i have a protocol setters.
How to get the two setters methods in other class?
I think the main problem is what do you mean by "getting"
If you want to have setters/getters for a Class you need to create them. For example, if you have a Class Person then you would need to create setter #age: and getter #age.
If you want to see all the accessors available for the class you need to check the protocol of the class. The accessing is the protocol where you can find messages which enable you to access the inner values of the object.
If you want to list all the messages available in some protocol you can use (#Pharo 10):
| selectors |
selectors := OrderedCollection allSelectorsInProtocol: #'accessing'.
Which will give you the following result:
a Set(#yourself #enclosedElement #atPin: #before: #first:
#lastIndexOf:startingAt:ifAbsent: #fourth #replaceAll:with: #basicSize
#allButFirst #at:ifAbsent: #atLast: #allButFirst: #lastIndexOf: #size
#indexOfAnyOf:startingAt: #ninth #before:ifAbsent: #at:put: #last
#after:ifAbsent: #basicAt:put: #at: #indexOf:ifAbsent: #atLast:put:
#indexOfAnyOf: #atAll: #replaceFrom:to:with:startingAt: #nextToLast
#replaceFrom:to:with: #middle #first #fifth #at:incrementBy:
#lastIndexOf:ifAbsent: #allButLast: #indexOfSubCollection:startingAt: #atAllPut:
#indexOf:startingAt: #basicAt: #identityIndexOf: #atAll:putAll: #third #indexOf:
#atLast:ifAbsent: #last: #after: #capacity #anyOne #seventh #allButLast
#lastIndexOfAnyOf:startingAt:ifAbsent: #sixth #second #eighth #atAll:put:
#from:to:put: #indexOfAnyOf:ifAbsent: #atWrap:put:
#indexOfAnyOf:startingAt:ifAbsent: #indexOf:startingAt:ifAbsent: #swap:with:
#identityIndexOf:ifAbsent: #atWrap: #indexOfSubCollection:startingAt:ifAbsent:)

Kotlin - Class Inheritance; Overriding properties

So I started learning Kotlin today and got to Classes when I noticed something weird.
When I run this code:
fun main() {
val dog_1 = Dog("Walrek", 7)
}
open class Animal(
open val type: String,
open val name: String,
open val speed: Int,
) {
init {
println("(Ani init) New animal: $type called $name with $speed speed")
}
}
class Dog(
override val name: String,
override val speed: Int,
): Animal("Dog", name, speed) {}
The init method will print out: (Ani init) New animal: Dog called null with 0 speed.
At first I thought I messed up somewhere and somehow I'm not passing the arguments properly into the Animal class, but then I added these two lines of code:
init {
println("(Dog init) New animal: $type called $name with $speed speed")
}
into Dog class, and
println(dog_1.name)
in the main function, and got this as an output:
(Ani init) New animal: Dog called null with 0 speed
(Dog init) New animal: Dog called Walrek with 7 speed
Walrek
So now my question is, why weren't the parameters passed into the Animal class, and yet I can access them as usual after creating the Dog instance?
You should be getting a compiler warning. You should never use open properties or functions at construction time (in init blocks or property declarations), because it causes this kind of unwanted behavior. It's just about the only way you can get a NullPointerException in Kotlin without using !!.
The reason it happens is that the superclass's constructor (which includes init blocks and property declarations) is run before the subclass. Since you override the property name in the subclass, the backing field of the property name has not yet been initialized by the time init of the superclass is called, so the property returns the default Java field value of null. In the case of speed, the field type is a Java primitive int so its default value is 0.
Note that in your specific example, there was no need to override these properties because you are passing these values to the superconstructor anyway. You could remove the open keyword before the properties and declare your Dog like:
class Dog(
name: String,
speed: Int,
): Animal("Dog", name, speed) {}
Then it will behave properly.

Use apply on a data class in Kotlin

I know how to use the apply function on a normal Kotlin class but have not been able to use it with a data class:
data class Person(name: String)
val person = Person().apply {
name = "Tony Stark"
}
I get a compile message of:
No value passed for parameter 'name'
The issue is that name is a constructor parameter only and not made a property, which is invalid for the data class concept anyway. Fix like this:
data class Person(val name: String)
The apply function works similar with any class. But there are some errors in your code snippet:
Parameter in Person constructor didn't mentioned as var or val, so there is no fields name in that class. It would be better to make it var to be able to change value.
You made class's constructor with 1 parameter, but trying to use empty constructor - it is error.

Checking textual representation of a class in a test?

Lets say I've created a class MyClass in Pharo Smalltalk...
If I in Workspace write:
MyClass new.
and select print-it (Ctrl-P), I get:
A MyClass
With a bit of tinkering with MyClass's printOn: method,
I could get a bit more, for example:
A MyClass with value: 5
+++
So comes my question... How can I make a test (instance of TestCase-class)
that checks that the textual-representation of MyObject - what I would get
if I did "MyObject new" and Print-It - is what it's supposed to be?
How do I get the textual representation so I can check it against a
string-constant with what it should be, when I do a self assert: equal: (or something similar) in my test?
For example that after using my cutomized printOn: method, it will
look something like
A MyClass with value: 5
Sorry for such a newbie question, but there goes...
To get the textual representation of an object you can send the message printString to the object. For example Object new printString will return you the string 'an Object'.
To create a test case you should create a subclass of TestCase:
TestCase subclass: #MyClassTestCase
instanceVariableNames: ''
classVariableNames: ''
package: 'MyTest-Package'
Then a test is a method that begins with test. For example the following test verifies the string representation of Object new:
testClassRepresentation
self assert: Object new printString equals: 'an Object'

making a constructor in Pharo or Smalltalk

I want to implement a small class in Pharo so I have done this:
Object subclass: #Person
instanceVariableNames: 'name age'
classVariableNames: ''
category: 'Test'
and I want to simulate a constructor so I have made a method like the following:
newName:aName newAge:aAge
"comment stating purpose of message"
name:=aName.
age:=aAge.
^self.
but when I want to call it from the playground in Pharo like this:
objPerson:=Person newName:'Carl' newAge: 10.
it is not recognized by Pharo, what am I doing wrong?
The expression Person newName: 'Carl' newAge: 10 is a message to the class object Person. Therefore you have to implement it on the class side of Person.
Your code needs to be tweaked like this
Person class >> newName: aName newAge: anAge
| person |
person := self new.
person name: aName.
person age: anAge.
^person
Note that in the code above self refers to the class because the method is on the class side. But since person is an instance of Person, the messages name: aName and age: anAge have to be defined on the instance side.
Therefore, on the instance side, you need to add the two setters:
Person >> name: aString
name := aString
Person >> age: anInteger
age := anInteger
With these three methods you should be able to run your example.
Some additional comments on the coding style:
Firstly, I would have chosen a different selector for the "constructor" method (in Smalltalk we call these "instance creation" methods). For instance,
Person class >> named: aString age: anInteger
^self new name: aString; age: anInteger
Secondly, there is no need to use the temporary person for the newly created instance as the expression self new already refers to such an instance.
Finally, note the use of the cascade syntax in
^self new name: aString; age: anInteger
which means that the message age: anInteger will be sent to the same receiver that name: aString, which in this case happens to be the new instance returned by self new.
Although in general I agree with Leandro's answer, it exposes accessors to the private properties.
Kent Beck in his Smalltalk Best Practice Patterns (which I highly recommend), proposes to use set as a prefix for the instance-side constructor, e.g.:
"add to class side to 'instance creation' protocol"
Person>>class name: aName age: anAge
^ self new
setName: aName age: anAge;
yourself
"add to instance side to 'initialization' protocol"
Person>>setName: aName age: anAge
name := aName.
age := anAge.
This way you can control whether you will or will not expose the properties.
Or you could stick with your original naming and just add new, which you forgot.
objPerson := Person new newName:'Carl' newAge: 10.