difference between double dispatch and the method explains here

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
29 messages Options
12
Reply | Threaded
Open this post in threaded view
|

difference between double dispatch and the method explains here

Roelof Wobben
Hello,

I just learned double dispatch.
And now for the Robot challenge of exercism Tim has pointed me to this
article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)



but I fail to see how the move method looks like in that article.
I had a conversation with Tim in the exercism channel and the way he
explains it, it looks like double dispatch for me.

Am I on the right track or do I oversee something here.

Roelof




Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

K K Subbu
On 06/04/19 4:49 PM, Roelof Wobben wrote:

> Hello,
>
> I just learned double dispatch.
> And now for the Robot challenge of exercism Tim has pointed me to this
> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>
> but I fail to see how the move method looks like in that article.
> I had a conversation with Tim in the exercism channel and the way he
> explains it, it looks like double dispatch for me.
>
> Am I on the right track or do I oversee something here.
unary methods like moveRight perform specific ops and are not
parametric, so only a single dispatch, depending on the receiver, is needed.

If you change it to move: aDistanceOrAngle, then performing requests
like "move: 3 cms" or "move: 30 degrees" will depend not only on the
receiver but also on the class of the argument. This would need double
dispatch (aka multiple polymorphism). The first dispatch would be based
on the receiver and the receiver's method would then dispatch it based
on the class of the argument (i.e. Distance>>move or Angle>>move )

HTH .. Subbu

Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Roelof Wobben
oke,

so I need a single Object here
that contains this in a initialize function :

north = Direction( 0, -1)
east  = Direction( 1,  0)
south = Direction( 0,  1)
west  = Direction(-1,  0)



oke, then time to figure out how to change my functions because I have the feeling that I still miss
some of the pieces: 

Robot >> move: aInstruction
	aInstruction = $R
		ifTrue: [ ^ self direction: (PositioningSystem new turnRight: direction) ].
	aInstruction = $L
		ifTrue: [ ^ self direction: (PositioningSystem new turnLeft: direction) ].
	^ self
		position:
			(PositioningSystem new moveForWard: position direction: self direction)



PositionSystem class >>  initialize
    Directions := {('north' -> (0 @ 1)).
    ('east' -> (1 @ 0)).
    ('south' -> (0 @ -1)).
    ('west' -> (-1 @ 0))}

PositionSystem >> turnRight: aDirection
    "comment stating purpose of message"

    | old |
    old := Directions detect: [ :b | b key = aDirection ].
    ^ (Directions after: old ifAbsent: [ Directions first ]) key

Roelof





Op 6-4-2019 om 15:15 schreef K K Subbu:
On 06/04/19 4:49 PM, Roelof Wobben wrote:
Hello,

I just learned double dispatch.
And now for the Robot challenge of exercism Tim has pointed me to this article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
but I fail to see how the move method looks like in that article.
I had a conversation with Tim in the exercism channel and the way he explains it, it looks like double dispatch for me.

Am I on the right track or do I oversee something here.
unary methods like moveRight perform specific ops and are not parametric, so only a single dispatch, depending on the receiver, is needed.

If you change it to move: aDistanceOrAngle, then performing requests like "move: 3 cms" or "move: 30 degrees" will depend not only on the receiver but also on the class of the argument. This would need double dispatch (aka multiple polymorphism). The first dispatch would be based on the receiver and the receiver's method would then dispatch it based on the class of the argument (i.e. Distance>>move or Angle>>move )

HTH .. Subbu



Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Roelof Wobben
In reply to this post by K K Subbu
Op 6-4-2019 om 15:15 schreef K K Subbu:

> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof


Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Richard O'Keefe
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof


Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Roelof Wobben
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof



Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Richard O'Keefe
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof



Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Roelof Wobben
Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof




Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Roelof Wobben
In reply to this post by Richard O'Keefe
Richard thanks.

One thing I do not see direct.

you said :


A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

for me a direction is the direction the robot is facing so something like north or east.

the challenge also wants a output like this :

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

so how do I "convert" the point you are using to the text.

Or do I misunderstood you somewhere wrong.

Roelof




Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof




Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Richard O'Keefe
The basic issue here is abstraction.
An instance of "Robot" in your program is not a
physical object.  How could it possibly point North,
South, or Nor-nor-west?  It cannot.
Its location and direction are abstract values
*metaphorically* related to real world notions
like position vectors and velocity vectors.
"North" in this program is not a real thing,
it is an *idea* which could be represented by
'North', 'north', #North, #north, $N, $n,
'Raki',  'raki',  #Raki,  #raki,  $R, $r,
137, (0@ -1), a picture of the star Polaris,
the colour red (the conventional colour for
that end of a compass needle which points north),
a sound recording of a lecture by Alfred North
Whitehead, or anything you please, as long as,
inside the program, it *acts* the way *you* want
"north" to act (which is not necessarily the way
the physical direction North acts, and in fact in
this case it most certainly is not).

Locations and movements in a 2D space are, in Smalltalk,
commonly represented by Points.  "Represented by."

As for this method:

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

PLEASE tell me that is not what they are actually using.
Let's start with
  (Dictionary new)
     add: k1 -> v1;
     ...
     add: kn -> vn;
     yourself
Did you know that sending #add: to a dictionary is not
portable?  Storing actual Association objects inside
Dictionaries was originally an encapsulation error and
remains a performance error, so there are Smalltalks
that do not make that mistake.  The *portable* way to
make a Dictionary is
    (Dictionary new)
       at: k1 put: v1;
       ...
       at: kn put: vn;
       yourself.

And why in the name of sanity are the keys *strings*
instead of *symbols*?  This is not Smalltalk.  It is
Javascript in drag.

Now exercism.io has a habit of insisting on particular
implementations.  For example, I completed the SML track,
and found that the test code ONLY worked with Poly and
not with any of the three SML implementations I already
had on my machine.  Since you are doing this in Pharo,
I take it that exercism.io will insist on the Smalltalk
track being done in Pharo, and in that case it is
*nauseating* to use a Dictionary when you could use a
Point.  Old-fashioned Smalltalk style would have been
to return something like
   #(<direction> <x> <y>)
e.g. #(north 1 0), and I still prefer that.

In fact *good* Smalltalk style for something like this
would be
test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
  robotSimulatorCalculator
    moveTo: 0@0;
    head: #north;
    obey: 'A'.
  self assert: robotSimulatorCalculator heading equals: #north.
  self assert: robotSimulatorCalculator location equals: 0@1.

-- We're starting to get the idea that identifiers like
robotSimulatorCalculator are not a very good idea when
simulatedRobot would do the job as well or better.

(This is also pointing us towards Betrand Meyer's
Command/Query Separation principle, but we shan't
go there today.)

This is important feedback to give to the exercism.io
people.  The test code should use a SMALLTALK interface,
not a warmed-over JAVASCRIPT interface.

Now, how do we map between direction *names* and
direction *points*?  Well, we have to start by
laying down clearly what we *mean* by the directions.

To move North one step is to add 1 to y and 0 to x.
(We know that from the appalling test case above.)
To move South one step is to add -1 to y and 0 to x.
(South is the opposite of North.)
To move East one step, oh we have a problem.
THIS NEEDS SPELLING OUT.  And one of the things the
exercism.io exercises are HORRIBLY BAD AT is specifying
the problem.  Nearly every single exercise I have tried,
I have been unable to tell what the problem is without
examining the test cases, and that is not the way
exercises are supposed to work.  (Yeah, that's why I'm
screaming about it.  I've taught a class using exercises
like this that were not of my writing and vague specifications
really upset the students.  People who had taken the class
under someone else several years before were still angry
about it.)

The geometric classes in Smalltalk were written to support
graphic user interfaces.  And in user interfaces, the y
coordinate increases DOWN.  So if we take the compass rose
and rotate it so that North is DOWN, it follows that
West is right and East is left.  So

To move East one step is to add -1 to x and 0 to y.
To move West one step is to add 1 to x and 0 to y.

The chances are excellent that the problem specification
is inconsistent with this.  Sigh.  Let's proceed, though.

North  0@1
South  0@ -1
East   -1@0
West   1@0


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]

nameToPoint: aSymbol
  aSymbol = #north ifTrue: [^0 @ 1].
  aSymbol = #south ifTrue: [^0 @ -1].
  aSymbol = #west  ifTrue: [^1 @ 0].
  aSymbol = #east  ifTrue: [^-1 @ 0].
  aSymbol error: 'not a compass direction in lower case'.

Another problem I had with exercism was a "Space-Age"
exercise where the README.md capitalised the planet names
but test_Space-Age.<whatever> insisted on lower case.
That might well happen here.

Just for grins,
Dictionary>>
asPoint
  ^(self at: 'x') @ (self at: 'y')

Point>>
asDictionary
  ^(Dictionary new)
     at: 'x' put: self x;
     at: 'y' put: self y;
     yourself




On Mon, 8 Apr 2019 at 22:15, Roelof Wobben <[hidden email]> wrote:
Richard thanks.

One thing I do not see direct.

you said :


A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

for me a direction is the direction the robot is facing so something like north or east.

the challenge also wants a output like this :

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

so how do I "convert" the point you are using to the text.

Or do I misunderstood you somewhere wrong.

Roelof




Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof




Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Roelof Wobben
yes,  this is a real  tests from the pharo track on exercism.io

I understand what you mean but maybe I overthinking things.
But if we have a robot facing north and the robot turns to the left  , im my oponion it faces now to the east.

like this test is saying :

test04_RotatesTheRobotsDirection90DegreesClockwiseChangesTheDirectionFromEastToSouth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'east'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'R'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'south';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 0;
                                yourself);
                yourself)


but I cannot come to the same outcome with this code :


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]


maybe exercism.io is not a good way to practice and learn smalltalk but I found not a better one. or smalltalk is not for me.

Roelof











Op 8-4-2019 om 16:44 schreef Richard O'Keefe:
The basic issue here is abstraction.
An instance of "Robot" in your program is not a
physical object.  How could it possibly point North,
South, or Nor-nor-west?  It cannot.
Its location and direction are abstract values
*metaphorically* related to real world notions
like position vectors and velocity vectors.
"North" in this program is not a real thing,
it is an *idea* which could be represented by
'North', 'north', #North, #north, $N, $n,
'Raki',  'raki',  #Raki,  #raki,  $R, $r,
137, (0@ -1), a picture of the star Polaris,
the colour red (the conventional colour for
that end of a compass needle which points north),
a sound recording of a lecture by Alfred North
Whitehead, or anything you please, as long as,
inside the program, it *acts* the way *you* want
"north" to act (which is not necessarily the way
the physical direction North acts, and in fact in
this case it most certainly is not).

Locations and movements in a 2D space are, in Smalltalk,
commonly represented by Points.  "Represented by."

As for this method:

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

PLEASE tell me that is not what they are actually using.
Let's start with
  (Dictionary new)
     add: k1 -> v1;
     ...
     add: kn -> vn;
     yourself
Did you know that sending #add: to a dictionary is not
portable?  Storing actual Association objects inside
Dictionaries was originally an encapsulation error and
remains a performance error, so there are Smalltalks
that do not make that mistake.  The *portable* way to
make a Dictionary is
    (Dictionary new)
       at: k1 put: v1;
       ...
       at: kn put: vn;
       yourself.

And why in the name of sanity are the keys *strings*
instead of *symbols*?  This is not Smalltalk.  It is
Javascript in drag.

Now exercism.io has a habit of insisting on particular
implementations.  For example, I completed the SML track,
and found that the test code ONLY worked with Poly and
not with any of the three SML implementations I already
had on my machine.  Since you are doing this in Pharo,
I take it that exercism.io will insist on the Smalltalk
track being done in Pharo, and in that case it is
*nauseating* to use a Dictionary when you could use a
Point.  Old-fashioned Smalltalk style would have been
to return something like
   #(<direction> <x> <y>)
e.g. #(north 1 0), and I still prefer that.

In fact *good* Smalltalk style for something like this
would be
test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
  robotSimulatorCalculator
    moveTo: 0@0;
    head: #north;
    obey: 'A'.
  self assert: robotSimulatorCalculator heading equals: #north.
  self assert: robotSimulatorCalculator location equals: 0@1.

-- We're starting to get the idea that identifiers like
robotSimulatorCalculator are not a very good idea when
simulatedRobot would do the job as well or better.

(This is also pointing us towards Betrand Meyer's
Command/Query Separation principle, but we shan't
go there today.)

This is important feedback to give to the exercism.io
people.  The test code should use a SMALLTALK interface,
not a warmed-over JAVASCRIPT interface.

Now, how do we map between direction *names* and
direction *points*?  Well, we have to start by
laying down clearly what we *mean* by the directions.

To move North one step is to add 1 to y and 0 to x.
(We know that from the appalling test case above.)
To move South one step is to add -1 to y and 0 to x.
(South is the opposite of North.)
To move East one step, oh we have a problem.
THIS NEEDS SPELLING OUT.  And one of the things the
exercism.io exercises are HORRIBLY BAD AT is specifying
the problem.  Nearly every single exercise I have tried,
I have been unable to tell what the problem is without
examining the test cases, and that is not the way
exercises are supposed to work.  (Yeah, that's why I'm
screaming about it.  I've taught a class using exercises
like this that were not of my writing and vague specifications
really upset the students.  People who had taken the class
under someone else several years before were still angry
about it.)

The geometric classes in Smalltalk were written to support
graphic user interfaces.  And in user interfaces, the y
coordinate increases DOWN.  So if we take the compass rose
and rotate it so that North is DOWN, it follows that
West is right and East is left.  So

To move East one step is to add -1 to x and 0 to y.
To move West one step is to add 1 to x and 0 to y.

The chances are excellent that the problem specification
is inconsistent with this.  Sigh.  Let's proceed, though.

North  0@1
South  0@ -1
East   -1@0
West   1@0


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]

nameToPoint: aSymbol
  aSymbol = #north ifTrue: [^0 @ 1].
  aSymbol = #south ifTrue: [^0 @ -1].
  aSymbol = #west  ifTrue: [^1 @ 0].
  aSymbol = #east  ifTrue: [^-1 @ 0].
  aSymbol error: 'not a compass direction in lower case'.

Another problem I had with exercism was a "Space-Age"
exercise where the README.md capitalised the planet names
but test_Space-Age.<whatever> insisted on lower case.
That might well happen here.

Just for grins,
Dictionary>>
asPoint
  ^(self at: 'x') @ (self at: 'y')

Point>>
asDictionary
  ^(Dictionary new)
     at: 'x' put: self x;
     at: 'y' put: self y;
     yourself




On Mon, 8 Apr 2019 at 22:15, Roelof Wobben <[hidden email]> wrote:
Richard thanks.

One thing I do not see direct.

you said :


A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

for me a direction is the direction the robot is facing so something like north or east.

the challenge also wants a output like this :

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

so how do I "convert" the point you are using to the text.

Or do I misunderstood you somewhere wrong.

Roelof




Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof





Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Richard O'Keefe
You are expected to use my code fragments for *ideas*,
not to incorporate them *literally* in your code.  As
I explained, *without seeing the specification*, I have
no way to tell whether the specification uses a left-handed
or right-handed coordinate system.

For what it's worth, here's a complete program in my
Smalltalk dialect.  It doesn't plug into the exercism
testing framework because I can do not know what it
looks like.  But if it makes the code more complicated
that this, it's doing it wrong.

require: 'geometry.st'  "Point"
require: 'print.st'     "OutputStream>>print:"
   
Object subclass: #Robot
  instanceVariableNames: 'position direction'
  poolDirectionaries:    'FileStream'

  methods for: 'initialising'
    pvtPostNew
      position  := 0@0.
      direction := 1@0.

  methods for: 'accessing'
    direction
      ^direction copy

    location
      ^location copy

    obey: commands
      commands do: [:each |
        each caseOf: {
          [$A] -> [position  := position  + direction].
          [$L] -> [direction := direction leftRotated].
          [$R] -> [direction := direction rightRotated]
        }].

  class methods for: 'main'
    start
      [StdIn atEnd] whileFalse: [
         |robot|
         robot := Robot new.
         Robot obey: StdIn nextLine.
         StdOut print: Robot location; cr].

On Tue, 9 Apr 2019 at 02:58, Roelof Wobben <[hidden email]> wrote:
yes,  this is a real  tests from the pharo track on exercism.io

I understand what you mean but maybe I overthinking things.
But if we have a robot facing north and the robot turns to the left  , im my oponion it faces now to the east.

like this test is saying :

test04_RotatesTheRobotsDirection90DegreesClockwiseChangesTheDirectionFromEastToSouth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'east'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'R'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'south';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 0;
                                yourself);
                yourself)


but I cannot come to the same outcome with this code :


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]


maybe exercism.io is not a good way to practice and learn smalltalk but I found not a better one. or smalltalk is not for me.

Roelof











Op 8-4-2019 om 16:44 schreef Richard O'Keefe:
The basic issue here is abstraction.
An instance of "Robot" in your program is not a
physical object.  How could it possibly point North,
South, or Nor-nor-west?  It cannot.
Its location and direction are abstract values
*metaphorically* related to real world notions
like position vectors and velocity vectors.
"North" in this program is not a real thing,
it is an *idea* which could be represented by
'North', 'north', #North, #north, $N, $n,
'Raki',  'raki',  #Raki,  #raki,  $R, $r,
137, (0@ -1), a picture of the star Polaris,
the colour red (the conventional colour for
that end of a compass needle which points north),
a sound recording of a lecture by Alfred North
Whitehead, or anything you please, as long as,
inside the program, it *acts* the way *you* want
"north" to act (which is not necessarily the way
the physical direction North acts, and in fact in
this case it most certainly is not).

Locations and movements in a 2D space are, in Smalltalk,
commonly represented by Points.  "Represented by."

As for this method:

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

PLEASE tell me that is not what they are actually using.
Let's start with
  (Dictionary new)
     add: k1 -> v1;
     ...
     add: kn -> vn;
     yourself
Did you know that sending #add: to a dictionary is not
portable?  Storing actual Association objects inside
Dictionaries was originally an encapsulation error and
remains a performance error, so there are Smalltalks
that do not make that mistake.  The *portable* way to
make a Dictionary is
    (Dictionary new)
       at: k1 put: v1;
       ...
       at: kn put: vn;
       yourself.

And why in the name of sanity are the keys *strings*
instead of *symbols*?  This is not Smalltalk.  It is
Javascript in drag.

Now exercism.io has a habit of insisting on particular
implementations.  For example, I completed the SML track,
and found that the test code ONLY worked with Poly and
not with any of the three SML implementations I already
had on my machine.  Since you are doing this in Pharo,
I take it that exercism.io will insist on the Smalltalk
track being done in Pharo, and in that case it is
*nauseating* to use a Dictionary when you could use a
Point.  Old-fashioned Smalltalk style would have been
to return something like
   #(<direction> <x> <y>)
e.g. #(north 1 0), and I still prefer that.

In fact *good* Smalltalk style for something like this
would be
test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
  robotSimulatorCalculator
    moveTo: 0@0;
    head: #north;
    obey: 'A'.
  self assert: robotSimulatorCalculator heading equals: #north.
  self assert: robotSimulatorCalculator location equals: 0@1.

-- We're starting to get the idea that identifiers like
robotSimulatorCalculator are not a very good idea when
simulatedRobot would do the job as well or better.

(This is also pointing us towards Betrand Meyer's
Command/Query Separation principle, but we shan't
go there today.)

This is important feedback to give to the exercism.io
people.  The test code should use a SMALLTALK interface,
not a warmed-over JAVASCRIPT interface.

Now, how do we map between direction *names* and
direction *points*?  Well, we have to start by
laying down clearly what we *mean* by the directions.

To move North one step is to add 1 to y and 0 to x.
(We know that from the appalling test case above.)
To move South one step is to add -1 to y and 0 to x.
(South is the opposite of North.)
To move East one step, oh we have a problem.
THIS NEEDS SPELLING OUT.  And one of the things the
exercism.io exercises are HORRIBLY BAD AT is specifying
the problem.  Nearly every single exercise I have tried,
I have been unable to tell what the problem is without
examining the test cases, and that is not the way
exercises are supposed to work.  (Yeah, that's why I'm
screaming about it.  I've taught a class using exercises
like this that were not of my writing and vague specifications
really upset the students.  People who had taken the class
under someone else several years before were still angry
about it.)

The geometric classes in Smalltalk were written to support
graphic user interfaces.  And in user interfaces, the y
coordinate increases DOWN.  So if we take the compass rose
and rotate it so that North is DOWN, it follows that
West is right and East is left.  So

To move East one step is to add -1 to x and 0 to y.
To move West one step is to add 1 to x and 0 to y.

The chances are excellent that the problem specification
is inconsistent with this.  Sigh.  Let's proceed, though.

North  0@1
South  0@ -1
East   -1@0
West   1@0


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]

nameToPoint: aSymbol
  aSymbol = #north ifTrue: [^0 @ 1].
  aSymbol = #south ifTrue: [^0 @ -1].
  aSymbol = #west  ifTrue: [^1 @ 0].
  aSymbol = #east  ifTrue: [^-1 @ 0].
  aSymbol error: 'not a compass direction in lower case'.

Another problem I had with exercism was a "Space-Age"
exercise where the README.md capitalised the planet names
but test_Space-Age.<whatever> insisted on lower case.
That might well happen here.

Just for grins,
Dictionary>>
asPoint
  ^(self at: 'x') @ (self at: 'y')

Point>>
asDictionary
  ^(Dictionary new)
     at: 'x' put: self x;
     at: 'y' put: self y;
     yourself




On Mon, 8 Apr 2019 at 22:15, Roelof Wobben <[hidden email]> wrote:
Richard thanks.

One thing I do not see direct.

you said :


A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

for me a direction is the direction the robot is facing so something like north or east.

the challenge also wants a output like this :

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

so how do I "convert" the point you are using to the text.

Or do I misunderstood you somewhere wrong.

Roelof




Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof





Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Roelof Wobben
Thanks,

for the discusson and lessons.

I will think about it and also think if smalltalk is for me.
I did the pharo Mooc and still have a lot of problems making the smalltalk way click in my head so I can solve little problems like this.

Out of coriousy what dialect do you use?


Op 8-4-2019 om 17:11 schreef Richard O'Keefe:
You are expected to use my code fragments for *ideas*,
not to incorporate them *literally* in your code.  As
I explained, *without seeing the specification*, I have
no way to tell whether the specification uses a left-handed
or right-handed coordinate system.

For what it's worth, here's a complete program in my
Smalltalk dialect.  It doesn't plug into the exercism
testing framework because I can do not know what it
looks like.  But if it makes the code more complicated
that this, it's doing it wrong.

require: 'geometry.st'  "Point"
require: 'print.st'     "OutputStream>>print:"
   
Object subclass: #Robot
  instanceVariableNames: 'position direction'
  poolDirectionaries:    'FileStream'

  methods for: 'initialising'
    pvtPostNew
      position  := 0@0.
      direction := 1@0.

  methods for: 'accessing'
    direction
      ^direction copy

    location
      ^location copy

    obey: commands
      commands do: [:each |
        each caseOf: {
          [$A] -> [position  := position  + direction].
          [$L] -> [direction := direction leftRotated].
          [$R] -> [direction := direction rightRotated]
        }].

  class methods for: 'main'
    start
      [StdIn atEnd] whileFalse: [
         |robot|
         robot := Robot new.
         Robot obey: StdIn nextLine.
         StdOut print: Robot location; cr].

On Tue, 9 Apr 2019 at 02:58, Roelof Wobben <[hidden email]> wrote:
yes,  this is a real  tests from the pharo track on exercism.io

I understand what you mean but maybe I overthinking things.
But if we have a robot facing north and the robot turns to the left  , im my oponion it faces now to the east.

like this test is saying :

test04_RotatesTheRobotsDirection90DegreesClockwiseChangesTheDirectionFromEastToSouth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'east'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'R'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'south';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 0;
                                yourself);
                yourself)


but I cannot come to the same outcome with this code :


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]


maybe exercism.io is not a good way to practice and learn smalltalk but I found not a better one. or smalltalk is not for me.

Roelof











Op 8-4-2019 om 16:44 schreef Richard O'Keefe:
The basic issue here is abstraction.
An instance of "Robot" in your program is not a
physical object.  How could it possibly point North,
South, or Nor-nor-west?  It cannot.
Its location and direction are abstract values
*metaphorically* related to real world notions
like position vectors and velocity vectors.
"North" in this program is not a real thing,
it is an *idea* which could be represented by
'North', 'north', #North, #north, $N, $n,
'Raki',  'raki',  #Raki,  #raki,  $R, $r,
137, (0@ -1), a picture of the star Polaris,
the colour red (the conventional colour for
that end of a compass needle which points north),
a sound recording of a lecture by Alfred North
Whitehead, or anything you please, as long as,
inside the program, it *acts* the way *you* want
"north" to act (which is not necessarily the way
the physical direction North acts, and in fact in
this case it most certainly is not).

Locations and movements in a 2D space are, in Smalltalk,
commonly represented by Points.  "Represented by."

As for this method:

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

PLEASE tell me that is not what they are actually using.
Let's start with
  (Dictionary new)
     add: k1 -> v1;
     ...
     add: kn -> vn;
     yourself
Did you know that sending #add: to a dictionary is not
portable?  Storing actual Association objects inside
Dictionaries was originally an encapsulation error and
remains a performance error, so there are Smalltalks
that do not make that mistake.  The *portable* way to
make a Dictionary is
    (Dictionary new)
       at: k1 put: v1;
       ...
       at: kn put: vn;
       yourself.

And why in the name of sanity are the keys *strings*
instead of *symbols*?  This is not Smalltalk.  It is
Javascript in drag.

Now exercism.io has a habit of insisting on particular
implementations.  For example, I completed the SML track,
and found that the test code ONLY worked with Poly and
not with any of the three SML implementations I already
had on my machine.  Since you are doing this in Pharo,
I take it that exercism.io will insist on the Smalltalk
track being done in Pharo, and in that case it is
*nauseating* to use a Dictionary when you could use a
Point.  Old-fashioned Smalltalk style would have been
to return something like
   #(<direction> <x> <y>)
e.g. #(north 1 0), and I still prefer that.

In fact *good* Smalltalk style for something like this
would be
test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
  robotSimulatorCalculator
    moveTo: 0@0;
    head: #north;
    obey: 'A'.
  self assert: robotSimulatorCalculator heading equals: #north.
  self assert: robotSimulatorCalculator location equals: 0@1.

-- We're starting to get the idea that identifiers like
robotSimulatorCalculator are not a very good idea when
simulatedRobot would do the job as well or better.

(This is also pointing us towards Betrand Meyer's
Command/Query Separation principle, but we shan't
go there today.)

This is important feedback to give to the exercism.io
people.  The test code should use a SMALLTALK interface,
not a warmed-over JAVASCRIPT interface.

Now, how do we map between direction *names* and
direction *points*?  Well, we have to start by
laying down clearly what we *mean* by the directions.

To move North one step is to add 1 to y and 0 to x.
(We know that from the appalling test case above.)
To move South one step is to add -1 to y and 0 to x.
(South is the opposite of North.)
To move East one step, oh we have a problem.
THIS NEEDS SPELLING OUT.  And one of the things the
exercism.io exercises are HORRIBLY BAD AT is specifying
the problem.  Nearly every single exercise I have tried,
I have been unable to tell what the problem is without
examining the test cases, and that is not the way
exercises are supposed to work.  (Yeah, that's why I'm
screaming about it.  I've taught a class using exercises
like this that were not of my writing and vague specifications
really upset the students.  People who had taken the class
under someone else several years before were still angry
about it.)

The geometric classes in Smalltalk were written to support
graphic user interfaces.  And in user interfaces, the y
coordinate increases DOWN.  So if we take the compass rose
and rotate it so that North is DOWN, it follows that
West is right and East is left.  So

To move East one step is to add -1 to x and 0 to y.
To move West one step is to add 1 to x and 0 to y.

The chances are excellent that the problem specification
is inconsistent with this.  Sigh.  Let's proceed, though.

North  0@1
South  0@ -1
East   -1@0
West   1@0


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]

nameToPoint: aSymbol
  aSymbol = #north ifTrue: [^0 @ 1].
  aSymbol = #south ifTrue: [^0 @ -1].
  aSymbol = #west  ifTrue: [^1 @ 0].
  aSymbol = #east  ifTrue: [^-1 @ 0].
  aSymbol error: 'not a compass direction in lower case'.

Another problem I had with exercism was a "Space-Age"
exercise where the README.md capitalised the planet names
but test_Space-Age.<whatever> insisted on lower case.
That might well happen here.

Just for grins,
Dictionary>>
asPoint
  ^(self at: 'x') @ (self at: 'y')

Point>>
asDictionary
  ^(Dictionary new)
     at: 'x' put: self x;
     at: 'y' put: self y;
     yourself




On Mon, 8 Apr 2019 at 22:15, Roelof Wobben <[hidden email]> wrote:
Richard thanks.

One thing I do not see direct.

you said :


A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

for me a direction is the direction the robot is facing so something like north or east.

the challenge also wants a output like this :

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

so how do I "convert" the point you are using to the text.

Or do I misunderstood you somewhere wrong.

Roelof




Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof






Reply | Threaded
Open this post in threaded view
|

Re: difference between double dispatch and the method explains here

Richard O'Keefe
On this laptop I have
 - Squeak
 - Pharo
 - GNU Smalltalk
 - VisualAge Smalltalk
 - VisualWorks Smalltalk
 - Smalltalk/X
plus some oddballs like susie, amber, and CSOM.
On another laptop I have
 - Strongtalk
 - Dolphin
And of course I have my own 'astc' Smalltalk-via-C compiler.
I have to say that Dolphin is easily the most *beautiful* Smalltalk
environment I've used.  (Yes, I'm the kind of person who has four
different C compilers on the same machine and uses them all.  You
don't want to know how many Javascript implementations...)

The important thing here is that there are at least two aspects to
"Smalltalk".  There is Smalltalk-the-approach-to-OO and there is
Smalltalk-the-many-related-but-different-IDEs.  When it comes to
productivity, the IDE is important.  Really important.  But when
it comes to thinking about programming and solving tasks like
exercism ones, it's the approach that matters.  And that approach
pays off in languages like Javascript and Ruby and Python as well.

I used to be a University lecturer.  Now I'm a (sub)contractor.
I used to see a LOT of student code that
 - had way too many classes
 - did not use existing well-known classes when it should
 - failed to encapsulate private state
 - put responsibilities in the wrong places
and that was Java code.  What prepared me to see such issues in Java?

Lots and lots of practice in Smalltalk.

And lots of reading Smalltalk, and figuring out what made it easy or
hard to read.

I do not know how much time you have on your hands,
but you might find it profitable to look at
specifically

Look at the bottom of that page for a list of 258
problems solved in Smalltalk.


On Tue, 9 Apr 2019 at 03:20, Roelof Wobben <[hidden email]> wrote:
Thanks,

for the discusson and lessons.

I will think about it and also think if smalltalk is for me.
I did the pharo Mooc and still have a lot of problems making the smalltalk way click in my head so I can solve little problems like this.

Out of coriousy what dialect do you use?


Op 8-4-2019 om 17:11 schreef Richard O'Keefe:
You are expected to use my code fragments for *ideas*,
not to incorporate them *literally* in your code.  As
I explained, *without seeing the specification*, I have
no way to tell whether the specification uses a left-handed
or right-handed coordinate system.

For what it's worth, here's a complete program in my
Smalltalk dialect.  It doesn't plug into the exercism
testing framework because I can do not know what it
looks like.  But if it makes the code more complicated
that this, it's doing it wrong.

require: 'geometry.st'  "Point"
require: 'print.st'     "OutputStream>>print:"
   
Object subclass: #Robot
  instanceVariableNames: 'position direction'
  poolDirectionaries:    'FileStream'

  methods for: 'initialising'
    pvtPostNew
      position  := 0@0.
      direction := 1@0.

  methods for: 'accessing'
    direction
      ^direction copy

    location
      ^location copy

    obey: commands
      commands do: [:each |
        each caseOf: {
          [$A] -> [position  := position  + direction].
          [$L] -> [direction := direction leftRotated].
          [$R] -> [direction := direction rightRotated]
        }].

  class methods for: 'main'
    start
      [StdIn atEnd] whileFalse: [
         |robot|
         robot := Robot new.
         Robot obey: StdIn nextLine.
         StdOut print: Robot location; cr].

On Tue, 9 Apr 2019 at 02:58, Roelof Wobben <[hidden email]> wrote:
yes,  this is a real  tests from the pharo track on exercism.io

I understand what you mean but maybe I overthinking things.
But if we have a robot facing north and the robot turns to the left  , im my oponion it faces now to the east.

like this test is saying :

test04_RotatesTheRobotsDirection90DegreesClockwiseChangesTheDirectionFromEastToSouth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'east'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'R'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'south';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 0;
                                yourself);
                yourself)


but I cannot come to the same outcome with this code :


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]


maybe exercism.io is not a good way to practice and learn smalltalk but I found not a better one. or smalltalk is not for me.

Roelof











Op 8-4-2019 om 16:44 schreef Richard O'Keefe:
The basic issue here is abstraction.
An instance of "Robot" in your program is not a
physical object.  How could it possibly point North,
South, or Nor-nor-west?  It cannot.
Its location and direction are abstract values
*metaphorically* related to real world notions
like position vectors and velocity vectors.
"North" in this program is not a real thing,
it is an *idea* which could be represented by
'North', 'north', #North, #north, $N, $n,
'Raki',  'raki',  #Raki,  #raki,  $R, $r,
137, (0@ -1), a picture of the star Polaris,
the colour red (the conventional colour for
that end of a compass needle which points north),
a sound recording of a lecture by Alfred North
Whitehead, or anything you please, as long as,
inside the program, it *acts* the way *you* want
"north" to act (which is not necessarily the way
the physical direction North acts, and in fact in
this case it most certainly is not).

Locations and movements in a 2D space are, in Smalltalk,
commonly represented by Points.  "Represented by."

As for this method:

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

PLEASE tell me that is not what they are actually using.
Let's start with
  (Dictionary new)
     add: k1 -> v1;
     ...
     add: kn -> vn;
     yourself
Did you know that sending #add: to a dictionary is not
portable?  Storing actual Association objects inside
Dictionaries was originally an encapsulation error and
remains a performance error, so there are Smalltalks
that do not make that mistake.  The *portable* way to
make a Dictionary is
    (Dictionary new)
       at: k1 put: v1;
       ...
       at: kn put: vn;
       yourself.

And why in the name of sanity are the keys *strings*
instead of *symbols*?  This is not Smalltalk.  It is
Javascript in drag.

Now exercism.io has a habit of insisting on particular
implementations.  For example, I completed the SML track,
and found that the test code ONLY worked with Poly and
not with any of the three SML implementations I already
had on my machine.  Since you are doing this in Pharo,
I take it that exercism.io will insist on the Smalltalk
track being done in Pharo, and in that case it is
*nauseating* to use a Dictionary when you could use a
Point.  Old-fashioned Smalltalk style would have been
to return something like
   #(<direction> <x> <y>)
e.g. #(north 1 0), and I still prefer that.

In fact *good* Smalltalk style for something like this
would be
test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
  robotSimulatorCalculator
    moveTo: 0@0;
    head: #north;
    obey: 'A'.
  self assert: robotSimulatorCalculator heading equals: #north.
  self assert: robotSimulatorCalculator location equals: 0@1.

-- We're starting to get the idea that identifiers like
robotSimulatorCalculator are not a very good idea when
simulatedRobot would do the job as well or better.

(This is also pointing us towards Betrand Meyer's
Command/Query Separation principle, but we shan't
go there today.)

This is important feedback to give to the exercism.io
people.  The test code should use a SMALLTALK interface,
not a warmed-over JAVASCRIPT interface.

Now, how do we map between direction *names* and
direction *points*?  Well, we have to start by
laying down clearly what we *mean* by the directions.

To move North one step is to add 1 to y and 0 to x.
(We know that from the appalling test case above.)
To move South one step is to add -1 to y and 0 to x.
(South is the opposite of North.)
To move East one step, oh we have a problem.
THIS NEEDS SPELLING OUT.  And one of the things the
exercism.io exercises are HORRIBLY BAD AT is specifying
the problem.  Nearly every single exercise I have tried,
I have been unable to tell what the problem is without
examining the test cases, and that is not the way
exercises are supposed to work.  (Yeah, that's why I'm
screaming about it.  I've taught a class using exercises
like this that were not of my writing and vague specifications
really upset the students.  People who had taken the class
under someone else several years before were still angry
about it.)

The geometric classes in Smalltalk were written to support
graphic user interfaces.  And in user interfaces, the y
coordinate increases DOWN.  So if we take the compass rose
and rotate it so that North is DOWN, it follows that
West is right and East is left.  So

To move East one step is to add -1 to x and 0 to y.
To move West one step is to add 1 to x and 0 to y.

The chances are excellent that the problem specification
is inconsistent with this.  Sigh.  Let's proceed, though.

North  0@1
South  0@ -1
East   -1@0
West   1@0


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]

nameToPoint: aSymbol
  aSymbol = #north ifTrue: [^0 @ 1].
  aSymbol = #south ifTrue: [^0 @ -1].
  aSymbol = #west  ifTrue: [^1 @ 0].
  aSymbol = #east  ifTrue: [^-1 @ 0].
  aSymbol error: 'not a compass direction in lower case'.

Another problem I had with exercism was a "Space-Age"
exercise where the README.md capitalised the planet names
but test_Space-Age.<whatever> insisted on lower case.
That might well happen here.

Just for grins,
Dictionary>>
asPoint
  ^(self at: 'x') @ (self at: 'y')

Point>>
asDictionary
  ^(Dictionary new)
     at: 'x' put: self x;
     at: 'y' put: self y;
     yourself




On Mon, 8 Apr 2019 at 22:15, Roelof Wobben <[hidden email]> wrote:
Richard thanks.

One thing I do not see direct.

you said :


A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

for me a direction is the direction the robot is facing so something like north or east.

the challenge also wants a output like this :

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

so how do I "convert" the point you are using to the text.

Or do I misunderstood you somewhere wrong.

Roelof




Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof






Reply | Threaded
Open this post in threaded view
|

[OT] (slightly) What makes other dialects "enjoyable" for you? (WAS: difference between double dispatch...)

EstebanLM
Hi,

Time to time I hear people like Richard saying “Dolphin is the dialect most beautiful Smalltalk he used” and others praising it in different levels. 
As Pharo “architect” (or whatever I am, but at least I’m sure I have to pay attention to the IDE :P), I’m interested to know what elements of Dolphin dialect you find “beautiful”, “enjoyable” and productive. 
What it is?

- the MVP?
- integration with Windows? The way this integration is done? (If so… how is it done?)

I am very interested on knowing this with some detail level. That doesn’t mean I will react and do something, but I want to have a better understanding and put it in my radar to take inspiration to enhance the Pharo experience :P

Esteban

On 10 Apr 2019, at 03:09, Richard O'Keefe <[hidden email]> wrote:

On this laptop I have
 - Squeak
 - Pharo
 - GNU Smalltalk
 - VisualAge Smalltalk
 - VisualWorks Smalltalk
 - Smalltalk/X
plus some oddballs like susie, amber, and CSOM.
On another laptop I have
 - Strongtalk
 - Dolphin
And of course I have my own 'astc' Smalltalk-via-C compiler.
I have to say that Dolphin is easily the most *beautiful* Smalltalk
environment I've used.  (Yes, I'm the kind of person who has four
different C compilers on the same machine and uses them all.  You
don't want to know how many Javascript implementations...)

The important thing here is that there are at least two aspects to
"Smalltalk".  There is Smalltalk-the-approach-to-OO and there is
Smalltalk-the-many-related-but-different-IDEs.  When it comes to
productivity, the IDE is important.  Really important.  But when
it comes to thinking about programming and solving tasks like
exercism ones, it's the approach that matters.  And that approach
pays off in languages like Javascript and Ruby and Python as well.

I used to be a University lecturer.  Now I'm a (sub)contractor.
I used to see a LOT of student code that
 - had way too many classes
 - did not use existing well-known classes when it should
 - failed to encapsulate private state
 - put responsibilities in the wrong places
and that was Java code.  What prepared me to see such issues in Java?

Lots and lots of practice in Smalltalk.

And lots of reading Smalltalk, and figuring out what made it easy or
hard to read.

I do not know how much time you have on your hands,
but you might find it profitable to look at
specifically

Look at the bottom of that page for a list of 258
problems solved in Smalltalk.


On Tue, 9 Apr 2019 at 03:20, Roelof Wobben <[hidden email]> wrote:
Thanks,

for the discusson and lessons.

I will think about it and also think if smalltalk is for me.
I did the pharo Mooc and still have a lot of problems making the smalltalk way click in my head so I can solve little problems like this.

Out of coriousy what dialect do you use?


Op 8-4-2019 om 17:11 schreef Richard O'Keefe:
You are expected to use my code fragments for *ideas*,
not to incorporate them *literally* in your code.  As
I explained, *without seeing the specification*, I have
no way to tell whether the specification uses a left-handed
or right-handed coordinate system.

For what it's worth, here's a complete program in my
Smalltalk dialect.  It doesn't plug into the exercism
testing framework because I can do not know what it
looks like.  But if it makes the code more complicated
that this, it's doing it wrong.

require: 'geometry.st'  "Point"
require: 'print.st'     "OutputStream>>print:"
   
Object subclass: #Robot
  instanceVariableNames: 'position direction'
  poolDirectionaries:    'FileStream'

  methods for: 'initialising'
    pvtPostNew
      position  := 0@0.
      direction := 1@0.

  methods for: 'accessing'
    direction
      ^direction copy

    location
      ^location copy

    obey: commands
      commands do: [:each |
        each caseOf: {
          [$A] -> [position  := position  + direction].
          [$L] -> [direction := direction leftRotated].
          [$R] -> [direction := direction rightRotated]
        }].

  class methods for: 'main'
    start
      [StdIn atEnd] whileFalse: [
         |robot|
         robot := Robot new.
         Robot obey: StdIn nextLine.
         StdOut print: Robot location; cr].

On Tue, 9 Apr 2019 at 02:58, Roelof Wobben <[hidden email]> wrote:
yes,  this is a real  tests from the pharo track on exercism.io

I understand what you mean but maybe I overthinking things.
But if we have a robot facing north and the robot turns to the left  , im my oponion it faces now to the east.

like this test is saying :

test04_RotatesTheRobotsDirection90DegreesClockwiseChangesTheDirectionFromEastToSouth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'east'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'R'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'south';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 0;
                                yourself);
                yourself)


but I cannot come to the same outcome with this code :


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]


maybe exercism.io is not a good way to practice and learn smalltalk but I found not a better one. or smalltalk is not for me.

Roelof











Op 8-4-2019 om 16:44 schreef Richard O'Keefe:
The basic issue here is abstraction.
An instance of "Robot" in your program is not a
physical object.  How could it possibly point North,
South, or Nor-nor-west?  It cannot.
Its location and direction are abstract values
*metaphorically* related to real world notions
like position vectors and velocity vectors.
"North" in this program is not a real thing,
it is an *idea* which could be represented by
'North', 'north', #North, #north, $N, $n,
'Raki',  'raki',  #Raki,  #raki,  $R, $r,
137, (0@ -1), a picture of the star Polaris,
the colour red (the conventional colour for
that end of a compass needle which points north),
a sound recording of a lecture by Alfred North
Whitehead, or anything you please, as long as,
inside the program, it *acts* the way *you* want
"north" to act (which is not necessarily the way
the physical direction North acts, and in fact in
this case it most certainly is not).

Locations and movements in a 2D space are, in Smalltalk,
commonly represented by Points.  "Represented by."

As for this method:

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

PLEASE tell me that is not what they are actually using.
Let's start with
  (Dictionary new)
     add: k1 -> v1;
     ...
     add: kn -> vn;
     yourself
Did you know that sending #add: to a dictionary is not
portable?  Storing actual Association objects inside
Dictionaries was originally an encapsulation error and
remains a performance error, so there are Smalltalks
that do not make that mistake.  The *portable* way to
make a Dictionary is
    (Dictionary new)
       at: k1 put: v1;
       ...
       at: kn put: vn;
       yourself.

And why in the name of sanity are the keys *strings*
instead of *symbols*?  This is not Smalltalk.  It is
Javascript in drag.

Now exercism.io has a habit of insisting on particular
implementations.  For example, I completed the SML track,
and found that the test code ONLY worked with Poly and
not with any of the three SML implementations I already
had on my machine.  Since you are doing this in Pharo,
I take it that exercism.io will insist on the Smalltalk
track being done in Pharo, and in that case it is
*nauseating* to use a Dictionary when you could use a
Point.  Old-fashioned Smalltalk style would have been
to return something like
   #(<direction> <x> <y>)
e.g. #(north 1 0), and I still prefer that.

In fact *good* Smalltalk style for something like this
would be
test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
  robotSimulatorCalculator
    moveTo: 0@0;
    head: #north;
    obey: 'A'.
  self assert: robotSimulatorCalculator heading equals: #north.
  self assert: robotSimulatorCalculator location equals: 0@1.

-- We're starting to get the idea that identifiers like
robotSimulatorCalculator are not a very good idea when
simulatedRobot would do the job as well or better.

(This is also pointing us towards Betrand Meyer's
Command/Query Separation principle, but we shan't
go there today.)

This is important feedback to give to the exercism.io
people.  The test code should use a SMALLTALK interface,
not a warmed-over JAVASCRIPT interface.

Now, how do we map between direction *names* and
direction *points*?  Well, we have to start by
laying down clearly what we *mean* by the directions.

To move North one step is to add 1 to y and 0 to x.
(We know that from the appalling test case above.)
To move South one step is to add -1 to y and 0 to x.
(South is the opposite of North.)
To move East one step, oh we have a problem.
THIS NEEDS SPELLING OUT.  And one of the things the
exercism.io exercises are HORRIBLY BAD AT is specifying
the problem.  Nearly every single exercise I have tried,
I have been unable to tell what the problem is without
examining the test cases, and that is not the way
exercises are supposed to work.  (Yeah, that's why I'm
screaming about it.  I've taught a class using exercises
like this that were not of my writing and vague specifications
really upset the students.  People who had taken the class
under someone else several years before were still angry
about it.)

The geometric classes in Smalltalk were written to support
graphic user interfaces.  And in user interfaces, the y
coordinate increases DOWN.  So if we take the compass rose
and rotate it so that North is DOWN, it follows that
West is right and East is left.  So

To move East one step is to add -1 to x and 0 to y.
To move West one step is to add 1 to x and 0 to y.

The chances are excellent that the problem specification
is inconsistent with this.  Sigh.  Let's proceed, though.

North  0@1
South  0@ -1
East   -1@0
West   1@0


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]

nameToPoint: aSymbol
  aSymbol = #north ifTrue: [^0 @ 1].
  aSymbol = #south ifTrue: [^0 @ -1].
  aSymbol = #west  ifTrue: [^1 @ 0].
  aSymbol = #east  ifTrue: [^-1 @ 0].
  aSymbol error: 'not a compass direction in lower case'.

Another problem I had with exercism was a "Space-Age"
exercise where the README.md capitalised the planet names
but test_Space-Age.<whatever> insisted on lower case.
That might well happen here.

Just for grins,
Dictionary>>
asPoint
  ^(self at: 'x') @ (self at: 'y')

Point>>
asDictionary
  ^(Dictionary new)
     at: 'x' put: self x;
     at: 'y' put: self y;
     yourself




On Mon, 8 Apr 2019 at 22:15, Roelof Wobben <[hidden email]> wrote:
Richard thanks.

One thing I do not see direct.

you said :


A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

for me a direction is the direction the robot is facing so something like north or east.

the challenge also wants a output like this :

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

so how do I "convert" the point you are using to the text.

Or do I misunderstood you somewhere wrong.

Roelof




Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof







Reply | Threaded
Open this post in threaded view
|

Re: [OT] (slightly) What makes other dialects "enjoyable" for you? (WAS: difference between double dispatch...)

jtuchel
Esteban,

I can, of course, only speak for myself and I must admit I never really used Dolphin for more extensive projects. What I always was envious of as a long-time VA Smalltalk-user was the nice look & feel of Dolphin. It just felt like Windows, acted and looked like Windows and offered all those neat little "newer" widgets (at the time) that IBM simply ignored and let their users dry off from.

The very same reason once made me excited about Ambrai Smalltalk, which was nicely integrated with MacOS X at the day.
Like Dolphin, which more or less capitulated from the .net transition, Ambrai died because it was based on Carbon and coult not easily be adapted to Cocoa.

I'd like to not start a flame war, but one thing that (I think) turns people away from most Smalltalks is that they neither look nor feel like a native application on any of the platforms they support. The only one that does is probably ObjectStudio. The web seemed to change the game, but then we fell behind all those frontend technologies that run in the Browser. Smalltalk is not very present/prominent in that field (yet?).

The first impression of Pharo and almost all other Smalltalks is probably that if that thing looks so "different" and maybe even "old fashioned", how can that possibly be a modern, highly productive development environment?

I personally like the Approach of WindowBuilder and/or SWT for building GUIs, but MVP also works very well and had a few nice side effects when it comes to exchanging teh presentation of a ViewModel. So MVP is probably also a sweet spot of Dolphin. I once was a big fan of IBM's Composition editor, but these days I'd go for WindowBuilder rather than CompEditor. I like the ability to change both code and visual representation, whatever fits my needs.


Just my 2 cents

Joachim






Am 10.04.19 um 08:25 schrieb Esteban Lorenzano:
Hi,

Time to time I hear people like Richard saying “Dolphin is the dialect most beautiful Smalltalk he used” and others praising it in different levels. 
As Pharo “architect” (or whatever I am, but at least I’m sure I have to pay attention to the IDE :P), I’m interested to know what elements of Dolphin dialect you find “beautiful”, “enjoyable” and productive. 
What it is?

- the MVP?
- integration with Windows? The way this integration is done? (If so… how is it done?)

I am very interested on knowing this with some detail level. That doesn’t mean I will react and do something, but I want to have a better understanding and put it in my radar to take inspiration to enhance the Pharo experience :P

Esteban

On 10 Apr 2019, at 03:09, Richard O'Keefe <[hidden email]> wrote:

On this laptop I have
 - Squeak
 - Pharo
 - GNU Smalltalk
 - VisualAge Smalltalk
 - VisualWorks Smalltalk
 - Smalltalk/X
plus some oddballs like susie, amber, and CSOM.
On another laptop I have
 - Strongtalk
 - Dolphin
And of course I have my own 'astc' Smalltalk-via-C compiler.
I have to say that Dolphin is easily the most *beautiful* Smalltalk
environment I've used.  (Yes, I'm the kind of person who has four
different C compilers on the same machine and uses them all.  You
don't want to know how many Javascript implementations...)

The important thing here is that there are at least two aspects to
"Smalltalk".  There is Smalltalk-the-approach-to-OO and there is
Smalltalk-the-many-related-but-different-IDEs.  When it comes to
productivity, the IDE is important.  Really important.  But when
it comes to thinking about programming and solving tasks like
exercism ones, it's the approach that matters.  And that approach
pays off in languages like Javascript and Ruby and Python as well.

I used to be a University lecturer.  Now I'm a (sub)contractor.
I used to see a LOT of student code that
 - had way too many classes
 - did not use existing well-known classes when it should
 - failed to encapsulate private state
 - put responsibilities in the wrong places
and that was Java code.  What prepared me to see such issues in Java?

Lots and lots of practice in Smalltalk.

And lots of reading Smalltalk, and figuring out what made it easy or
hard to read.

I do not know how much time you have on your hands,
but you might find it profitable to look at
specifically

Look at the bottom of that page for a list of 258
problems solved in Smalltalk.


On Tue, 9 Apr 2019 at 03:20, Roelof Wobben <[hidden email]> wrote:
Thanks,

for the discusson and lessons.

I will think about it and also think if smalltalk is for me.
I did the pharo Mooc and still have a lot of problems making the smalltalk way click in my head so I can solve little problems like this.

Out of coriousy what dialect do you use?


Op 8-4-2019 om 17:11 schreef Richard O'Keefe:
You are expected to use my code fragments for *ideas*,
not to incorporate them *literally* in your code.  As
I explained, *without seeing the specification*, I have
no way to tell whether the specification uses a left-handed
or right-handed coordinate system.

For what it's worth, here's a complete program in my
Smalltalk dialect.  It doesn't plug into the exercism
testing framework because I can do not know what it
looks like.  But if it makes the code more complicated
that this, it's doing it wrong.

require: 'geometry.st'  "Point"
require: 'print.st'     "OutputStream>>print:"
   
Object subclass: #Robot
  instanceVariableNames: 'position direction'
  poolDirectionaries:    'FileStream'

  methods for: 'initialising'
    pvtPostNew
      position  := 0@0.
      direction := 1@0.

  methods for: 'accessing'
    direction
      ^direction copy

    location
      ^location copy

    obey: commands
      commands do: [:each |
        each caseOf: {
          [$A] -> [position  := position  + direction].
          [$L] -> [direction := direction leftRotated].
          [$R] -> [direction := direction rightRotated]
        }].

  class methods for: 'main'
    start
      [StdIn atEnd] whileFalse: [
         |robot|
         robot := Robot new.
         Robot obey: StdIn nextLine.
         StdOut print: Robot location; cr].

On Tue, 9 Apr 2019 at 02:58, Roelof Wobben <[hidden email]> wrote:
yes,  this is a real  tests from the pharo track on exercism.io

I understand what you mean but maybe I overthinking things.
But if we have a robot facing north and the robot turns to the left  , im my oponion it faces now to the east.

like this test is saying :

test04_RotatesTheRobotsDirection90DegreesClockwiseChangesTheDirectionFromEastToSouth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'east'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'R'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'south';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 0;
                                yourself);
                yourself)


but I cannot come to the same outcome with this code :


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]


maybe exercism.io is not a good way to practice and learn smalltalk but I found not a better one. or smalltalk is not for me.

Roelof











Op 8-4-2019 om 16:44 schreef Richard O'Keefe:
The basic issue here is abstraction.
An instance of "Robot" in your program is not a
physical object.  How could it possibly point North,
South, or Nor-nor-west?  It cannot.
Its location and direction are abstract values
*metaphorically* related to real world notions
like position vectors and velocity vectors.
"North" in this program is not a real thing,
it is an *idea* which could be represented by
'North', 'north', #North, #north, $N, $n,
'Raki',  'raki',  #Raki,  #raki,  $R, $r,
137, (0@ -1), a picture of the star Polaris,
the colour red (the conventional colour for
that end of a compass needle which points north),
a sound recording of a lecture by Alfred North
Whitehead, or anything you please, as long as,
inside the program, it *acts* the way *you* want
"north" to act (which is not necessarily the way
the physical direction North acts, and in fact in
this case it most certainly is not).

Locations and movements in a 2D space are, in Smalltalk,
commonly represented by Points.  "Represented by."

As for this method:

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

PLEASE tell me that is not what they are actually using.
Let's start with
  (Dictionary new)
     add: k1 -> v1;
     ...
     add: kn -> vn;
     yourself
Did you know that sending #add: to a dictionary is not
portable?  Storing actual Association objects inside
Dictionaries was originally an encapsulation error and
remains a performance error, so there are Smalltalks
that do not make that mistake.  The *portable* way to
make a Dictionary is
    (Dictionary new)
       at: k1 put: v1;
       ...
       at: kn put: vn;
       yourself.

And why in the name of sanity are the keys *strings*
instead of *symbols*?  This is not Smalltalk.  It is
Javascript in drag.

Now exercism.io has a habit of insisting on particular
implementations.  For example, I completed the SML track,
and found that the test code ONLY worked with Poly and
not with any of the three SML implementations I already
had on my machine.  Since you are doing this in Pharo,
I take it that exercism.io will insist on the Smalltalk
track being done in Pharo, and in that case it is
*nauseating* to use a Dictionary when you could use a
Point.  Old-fashioned Smalltalk style would have been
to return something like
   #(<direction> <x> <y>)
e.g. #(north 1 0), and I still prefer that.

In fact *good* Smalltalk style for something like this
would be
test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
  robotSimulatorCalculator
    moveTo: 0@0;
    head: #north;
    obey: 'A'.
  self assert: robotSimulatorCalculator heading equals: #north.
  self assert: robotSimulatorCalculator location equals: 0@1.

-- We're starting to get the idea that identifiers like
robotSimulatorCalculator are not a very good idea when
simulatedRobot would do the job as well or better.

(This is also pointing us towards Betrand Meyer's
Command/Query Separation principle, but we shan't
go there today.)

This is important feedback to give to the exercism.io
people.  The test code should use a SMALLTALK interface,
not a warmed-over JAVASCRIPT interface.

Now, how do we map between direction *names* and
direction *points*?  Well, we have to start by
laying down clearly what we *mean* by the directions.

To move North one step is to add 1 to y and 0 to x.
(We know that from the appalling test case above.)
To move South one step is to add -1 to y and 0 to x.
(South is the opposite of North.)
To move East one step, oh we have a problem.
THIS NEEDS SPELLING OUT.  And one of the things the
exercism.io exercises are HORRIBLY BAD AT is specifying
the problem.  Nearly every single exercise I have tried,
I have been unable to tell what the problem is without
examining the test cases, and that is not the way
exercises are supposed to work.  (Yeah, that's why I'm
screaming about it.  I've taught a class using exercises
like this that were not of my writing and vague specifications
really upset the students.  People who had taken the class
under someone else several years before were still angry
about it.)

The geometric classes in Smalltalk were written to support
graphic user interfaces.  And in user interfaces, the y
coordinate increases DOWN.  So if we take the compass rose
and rotate it so that North is DOWN, it follows that
West is right and East is left.  So

To move East one step is to add -1 to x and 0 to y.
To move West one step is to add 1 to x and 0 to y.

The chances are excellent that the problem specification
is inconsistent with this.  Sigh.  Let's proceed, though.

North  0@1
South  0@ -1
East   -1@0
West   1@0


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]

nameToPoint: aSymbol
  aSymbol = #north ifTrue: [^0 @ 1].
  aSymbol = #south ifTrue: [^0 @ -1].
  aSymbol = #west  ifTrue: [^1 @ 0].
  aSymbol = #east  ifTrue: [^-1 @ 0].
  aSymbol error: 'not a compass direction in lower case'.

Another problem I had with exercism was a "Space-Age"
exercise where the README.md capitalised the planet names
but test_Space-Age.<whatever> insisted on lower case.
That might well happen here.

Just for grins,
Dictionary>>
asPoint
  ^(self at: 'x') @ (self at: 'y')

Point>>
asDictionary
  ^(Dictionary new)
     at: 'x' put: self x;
     at: 'y' put: self y;
     yourself




On Mon, 8 Apr 2019 at 22:15, Roelof Wobben <[hidden email]> wrote:
Richard thanks.

One thing I do not see direct.

you said :


A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

for me a direction is the direction the robot is facing so something like north or east.

the challenge also wants a output like this :

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

so how do I "convert" the point you are using to the text.

Or do I misunderstood you somewhere wrong.

Roelof




Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof








-- 
-----------------------------------------------------------------------
Objektfabrik Joachim Tuchel          [hidden email]
Fliederweg 1                         http://www.objektfabrik.de
D-71640 Ludwigsburg                  http://joachimtuchel.wordpress.com
Telefon: +49 7141 56 10 86 0         Fax: +49 7141 56 10 86 1


Reply | Threaded
Open this post in threaded view
|

Re: [OT] (slightly) What makes other dialects "enjoyable" for you? (WAS: difference between double dispatch...)

Richard O'Keefe
In reply to this post by EstebanLM
There are three questions there: "beautiful", "enjoyable",
and "productive".

On the subject of beauty, I would classify this way:
(0) Ugly.  VisualAge.  Now the VisualAge people have
    over the years put a *huge* amount of work into
    making VAST a serious contender for commercial
    programming.  But aesthetics of the basic IDE,
    no.  I would say that the original Smalltalk-80
    was more attractive.  Dark grey text on medium
    grey background is not the most readable.  I
    have attached a screenshot of a browser window.

(1) VisualWorks.  This has come a long way, and it
    isn't bad.  If you look at the screenshot for
    that, you'll see that the text is fuzzy, and
    part of "beauty" for me is effortlessly readable
    text.

(2) GNU Smalltalk gst-browser goes to the opposite extreme
    from VAST and VW.  They go for lots of separate cramped
    little windows.  GST takes over your whole screen for
    one browser window.  What makes GST poor is that at
    least under Ubuntu 18.04 it spews a huge number of
    error messages on the terminal you start it from.

(3) Squeak and Pharo.  At the level of aesthetics, I
    find Squeak 5 and Pharo 7 about the same.  They
    pass the "effortlessly readable" test.  Pharo has
    an edge in the individual tools; Squeak feels
    more "playful", if that makes sense.

(4) Dolphin has simply had a lot of work put into two
    things:  Windows integration, and aesthetics.  It
    just *looks* gorgeous.  It looks as though it has
    been designed by a professional graphic designer,
    yet nobody who has read the coloured books or
    Inside Smalltalk should have any trouble with the
    IDE.  There is one place where it falls down, and
    that is that no attention seems to have been given
    to the spelling of comments, hint hint.

Enjoyability.
(0) Not only is reading a pain in VAST, but on Linux I
    find that the shortcut keys do not work.  This is
    surprisingly unpleasant.  The browser is not a five
    pane browser.  The methods are divided into layers
    by package.  A method may appear in more than one
    layer.

(1) VW isn't too bad, but there are some glitches.
    For example, Browse|Implementors of selector|
    scroll through an alphabetic list and guess|
    whoops, the list has gone!  Yes, there's a list
    of implementors, but I guessed wrong, I need to
    start again from the beginning.  One thing that
    rather irritated me some years ago was wanting
    to use some ANSI methods, finding that they were
    not there, implementing them myself, and then
    discovering that there was an ANSI compatibility
    package that was not loaded by default.

    Now here is an idea.  If I type a shell command
    in linux, and mistype it, it tells me which
    packages define a command by that name.  E.g.,
    % mb foo.st foo.ost
    Command 'mb' not found, but can be installed with:
    sudo apt install mrbayes
    Wouldn't it be nice if using an undefined class or
    selector meant you were offered a list of "well known"
    packages that define it?

(2) I haven't used the GNU Smalltalk GUI enough to comment.

(3) My main issue with Pharo is that I never know from one
    release to the next what a facility will be called or
    where it will be in the menu hierarchy.  I also detest
    editors that by default *force* their style on me, and
    I find that displaying a selector as you type it just
    gets in my way, often obscuring something I need to see.
    However, after spending about an hour to customise
    more settings than I ever want to know about, it's a
    pleasure to use.  (How are beginners supposed to guess
    that 'Iceberg' has something to do with version control?)

(4) Dolphin's browser has a minor issue or two which I have
    forgotten, but mostly it's a joy to use.  The main thing
    is that so *much* effort has gone into the GUI and into
    Windows integration that there's rather less in other
    things.

Productivity.
    Here so much depends on what you are doing and whether
    there is a package available for it that I will say no more.




On Wed, 10 Apr 2019 at 18:26, Esteban Lorenzano <[hidden email]> wrote:
Hi,

Time to time I hear people like Richard saying “Dolphin is the dialect most beautiful Smalltalk he used” and others praising it in different levels. 
As Pharo “architect” (or whatever I am, but at least I’m sure I have to pay attention to the IDE :P), I’m interested to know what elements of Dolphin dialect you find “beautiful”, “enjoyable” and productive. 
What it is?

- the MVP?
- integration with Windows? The way this integration is done? (If so… how is it done?)

I am very interested on knowing this with some detail level. That doesn’t mean I will react and do something, but I want to have a better understanding and put it in my radar to take inspiration to enhance the Pharo experience :P

Esteban

On 10 Apr 2019, at 03:09, Richard O'Keefe <[hidden email]> wrote:

On this laptop I have
 - Squeak
 - Pharo
 - GNU Smalltalk
 - VisualAge Smalltalk
 - VisualWorks Smalltalk
 - Smalltalk/X
plus some oddballs like susie, amber, and CSOM.
On another laptop I have
 - Strongtalk
 - Dolphin
And of course I have my own 'astc' Smalltalk-via-C compiler.
I have to say that Dolphin is easily the most *beautiful* Smalltalk
environment I've used.  (Yes, I'm the kind of person who has four
different C compilers on the same machine and uses them all.  You
don't want to know how many Javascript implementations...)

The important thing here is that there are at least two aspects to
"Smalltalk".  There is Smalltalk-the-approach-to-OO and there is
Smalltalk-the-many-related-but-different-IDEs.  When it comes to
productivity, the IDE is important.  Really important.  But when
it comes to thinking about programming and solving tasks like
exercism ones, it's the approach that matters.  And that approach
pays off in languages like Javascript and Ruby and Python as well.

I used to be a University lecturer.  Now I'm a (sub)contractor.
I used to see a LOT of student code that
 - had way too many classes
 - did not use existing well-known classes when it should
 - failed to encapsulate private state
 - put responsibilities in the wrong places
and that was Java code.  What prepared me to see such issues in Java?

Lots and lots of practice in Smalltalk.

And lots of reading Smalltalk, and figuring out what made it easy or
hard to read.

I do not know how much time you have on your hands,
but you might find it profitable to look at
specifically

Look at the bottom of that page for a list of 258
problems solved in Smalltalk.


On Tue, 9 Apr 2019 at 03:20, Roelof Wobben <[hidden email]> wrote:
Thanks,

for the discusson and lessons.

I will think about it and also think if smalltalk is for me.
I did the pharo Mooc and still have a lot of problems making the smalltalk way click in my head so I can solve little problems like this.

Out of coriousy what dialect do you use?


Op 8-4-2019 om 17:11 schreef Richard O'Keefe:
You are expected to use my code fragments for *ideas*,
not to incorporate them *literally* in your code.  As
I explained, *without seeing the specification*, I have
no way to tell whether the specification uses a left-handed
or right-handed coordinate system.

For what it's worth, here's a complete program in my
Smalltalk dialect.  It doesn't plug into the exercism
testing framework because I can do not know what it
looks like.  But if it makes the code more complicated
that this, it's doing it wrong.

require: 'geometry.st'  "Point"
require: 'print.st'     "OutputStream>>print:"
   
Object subclass: #Robot
  instanceVariableNames: 'position direction'
  poolDirectionaries:    'FileStream'

  methods for: 'initialising'
    pvtPostNew
      position  := 0@0.
      direction := 1@0.

  methods for: 'accessing'
    direction
      ^direction copy

    location
      ^location copy

    obey: commands
      commands do: [:each |
        each caseOf: {
          [$A] -> [position  := position  + direction].
          [$L] -> [direction := direction leftRotated].
          [$R] -> [direction := direction rightRotated]
        }].

  class methods for: 'main'
    start
      [StdIn atEnd] whileFalse: [
         |robot|
         robot := Robot new.
         Robot obey: StdIn nextLine.
         StdOut print: Robot location; cr].

On Tue, 9 Apr 2019 at 02:58, Roelof Wobben <[hidden email]> wrote:
yes,  this is a real  tests from the pharo track on exercism.io

I understand what you mean but maybe I overthinking things.
But if we have a robot facing north and the robot turns to the left  , im my oponion it faces now to the east.

like this test is saying :

test04_RotatesTheRobotsDirection90DegreesClockwiseChangesTheDirectionFromEastToSouth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'east'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'R'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'south';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 0;
                                yourself);
                yourself)


but I cannot come to the same outcome with this code :


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]


maybe exercism.io is not a good way to practice and learn smalltalk but I found not a better one. or smalltalk is not for me.

Roelof











Op 8-4-2019 om 16:44 schreef Richard O'Keefe:
The basic issue here is abstraction.
An instance of "Robot" in your program is not a
physical object.  How could it possibly point North,
South, or Nor-nor-west?  It cannot.
Its location and direction are abstract values
*metaphorically* related to real world notions
like position vectors and velocity vectors.
"North" in this program is not a real thing,
it is an *idea* which could be represented by
'North', 'north', #North, #north, $N, $n,
'Raki',  'raki',  #Raki,  #raki,  $R, $r,
137, (0@ -1), a picture of the star Polaris,
the colour red (the conventional colour for
that end of a compass needle which points north),
a sound recording of a lecture by Alfred North
Whitehead, or anything you please, as long as,
inside the program, it *acts* the way *you* want
"north" to act (which is not necessarily the way
the physical direction North acts, and in fact in
this case it most certainly is not).

Locations and movements in a 2D space are, in Smalltalk,
commonly represented by Points.  "Represented by."

As for this method:

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

PLEASE tell me that is not what they are actually using.
Let's start with
  (Dictionary new)
     add: k1 -> v1;
     ...
     add: kn -> vn;
     yourself
Did you know that sending #add: to a dictionary is not
portable?  Storing actual Association objects inside
Dictionaries was originally an encapsulation error and
remains a performance error, so there are Smalltalks
that do not make that mistake.  The *portable* way to
make a Dictionary is
    (Dictionary new)
       at: k1 put: v1;
       ...
       at: kn put: vn;
       yourself.

And why in the name of sanity are the keys *strings*
instead of *symbols*?  This is not Smalltalk.  It is
Javascript in drag.

Now exercism.io has a habit of insisting on particular
implementations.  For example, I completed the SML track,
and found that the test code ONLY worked with Poly and
not with any of the three SML implementations I already
had on my machine.  Since you are doing this in Pharo,
I take it that exercism.io will insist on the Smalltalk
track being done in Pharo, and in that case it is
*nauseating* to use a Dictionary when you could use a
Point.  Old-fashioned Smalltalk style would have been
to return something like
   #(<direction> <x> <y>)
e.g. #(north 1 0), and I still prefer that.

In fact *good* Smalltalk style for something like this
would be
test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
  robotSimulatorCalculator
    moveTo: 0@0;
    head: #north;
    obey: 'A'.
  self assert: robotSimulatorCalculator heading equals: #north.
  self assert: robotSimulatorCalculator location equals: 0@1.

-- We're starting to get the idea that identifiers like
robotSimulatorCalculator are not a very good idea when
simulatedRobot would do the job as well or better.

(This is also pointing us towards Betrand Meyer's
Command/Query Separation principle, but we shan't
go there today.)

This is important feedback to give to the exercism.io
people.  The test code should use a SMALLTALK interface,
not a warmed-over JAVASCRIPT interface.

Now, how do we map between direction *names* and
direction *points*?  Well, we have to start by
laying down clearly what we *mean* by the directions.

To move North one step is to add 1 to y and 0 to x.
(We know that from the appalling test case above.)
To move South one step is to add -1 to y and 0 to x.
(South is the opposite of North.)
To move East one step, oh we have a problem.
THIS NEEDS SPELLING OUT.  And one of the things the
exercism.io exercises are HORRIBLY BAD AT is specifying
the problem.  Nearly every single exercise I have tried,
I have been unable to tell what the problem is without
examining the test cases, and that is not the way
exercises are supposed to work.  (Yeah, that's why I'm
screaming about it.  I've taught a class using exercises
like this that were not of my writing and vague specifications
really upset the students.  People who had taken the class
under someone else several years before were still angry
about it.)

The geometric classes in Smalltalk were written to support
graphic user interfaces.  And in user interfaces, the y
coordinate increases DOWN.  So if we take the compass rose
and rotate it so that North is DOWN, it follows that
West is right and East is left.  So

To move East one step is to add -1 to x and 0 to y.
To move West one step is to add 1 to x and 0 to y.

The chances are excellent that the problem specification
is inconsistent with this.  Sigh.  Let's proceed, though.

North  0@1
South  0@ -1
East   -1@0
West   1@0


pointToName: aPoint
  ^aPoint x isZero
     ifTrue:  [aPoint y > 0 ifTrue: [#north] ifFalse: [#south]]
     ifFalse: [aPoint x > 0 ifTrue: [#west ] ifFalse: [#east ]]

nameToPoint: aSymbol
  aSymbol = #north ifTrue: [^0 @ 1].
  aSymbol = #south ifTrue: [^0 @ -1].
  aSymbol = #west  ifTrue: [^1 @ 0].
  aSymbol = #east  ifTrue: [^-1 @ 0].
  aSymbol error: 'not a compass direction in lower case'.

Another problem I had with exercism was a "Space-Age"
exercise where the README.md capitalised the planet names
but test_Space-Age.<whatever> insisted on lower case.
That might well happen here.

Just for grins,
Dictionary>>
asPoint
  ^(self at: 'x') @ (self at: 'y')

Point>>
asDictionary
  ^(Dictionary new)
     at: 'x' put: self x;
     at: 'y' put: self y;
     yourself




On Mon, 8 Apr 2019 at 22:15, Roelof Wobben <[hidden email]> wrote:
Richard thanks.

One thing I do not see direct.

you said :


A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

for me a direction is the direction the robot is facing so something like north or east.

the challenge also wants a output like this :

test11_MovesTheRobotForward1SpaceInTheDirectionItIsPointingIncreasesTheYCoordinateOneWhenFacingNorth
    | result |
    result := robotSimulatorCalculator
        moveDirection: 'north'
        position:
            (Dictionary new
                add: 'x' -> 0;
                add: 'y' -> 0;
                yourself)
        instructions: 'A'.
    self
        assert: result
        equals:
            (Dictionary new
                add: 'direction' -> 'north';
                add:
                    'position'
                        ->
                            (Dictionary new
                                add: 'x' -> 0;
                                add: 'y' -> 1;
                                yourself);
                yourself)

so how do I "convert" the point you are using to the text.

Or do I misunderstood you somewhere wrong.

Roelof




Op 8-4-2019 om 10:57 schreef Richard O'Keefe:
One thing I have often seen and lamented is students
writing excessively complicated code with way too many
classes.  There is a huge difference between
  "A Robot knows its position and direction."
and
  "A Robot has-a Position and has-a Direction."
The first is the important one.  The second is
an over-commitment to too many classses.  For a
problem like this, you really really do not want
a Direction class, and you certainly have no use
for double dispatch.

A position can be represented by a pair of integers
x, y.  It could also be represented by a Point with
integer components.

A direction could be represented by a pair of integers
dx, dy such that |dx|+|dy| = 1.  It could also be
represented by a Point with integer components.

For movement, you need to be able to add the direction
to the location, which could be simply
x := x + dx.  y := y + dy.
or it could be
position := position + direction.
For turning, you need to be able to rotate a direction
vector by ninety degrees.  Now it so happens that
Point has methods #leftRotated and #rightRotated.

So we can do the following:
   a Robot has position (a Point) and direction (aPoint)
   position := 0 @ 0.
   direction := 0 @ 1.
To move forward without turning:
   position := position + direction.
To turn left without moving:
   direction := direction leftRotated.
To turn right without moving:
   direction := direction rightRotated.
To obey a sequence of characters, commands:
   commands do: [:each |
      each caseOf: {
         [$A] -> [--move forward--].
         [$L] -> [--turn left--].
         [$R] -> [--turn right--]
      }].


One of the key ideas in extreme programming is
"You Ain't Gonna Need It", abbreviated to YAGNI!
The idea is *DON'T* generalise beyond your immediate
needs.  In this case, for example, the likelihood of
*this* program needing to deal with more general
kinds of movement is ZERO.  And the only reason for
using Point here instead of just using a few simple
assignment statements is that Point already exists,
so costs nothing to write, and as a familiar class,
code using it should be easy to read.

If someone challenges you to do something counter-productive,
refuse the challenge.

On Mon, 8 Apr 2019 at 17:21, Roelof Wobben <[hidden email]> wrote:
I can try to explain what I trying to solve.

I have a Robot which can turn left,  turn right or moveForward.

now I have a string like 'LAR'

that means the robot needs to turn left (l) , move forward one place (A) and turn left.
and I have to keep track to which direction the robot is facing and on which coordinate it stands.

so to summarize with the above string

lets say the robot is facing north on coordinate (0,0)
then it has to turn left , so its facing east and still on coordinate (0,0)
then it has to move forward, so its still  facing east but are on coordinate(0,1)
then it has to turn right, so its facing north and on coordinate (0,1)

and TimMacKinnon has challenged me to do this with double dispatch.

So I think now I need a object Direction, a sub object North and a sub - sub object TurnLeft, turnRight and moveForward.

So I can use double dispath first the direction North, East, South, West
and then use double dispatch to find the right move.

Roelof





Op 8-4-2019 om 06:50 schreef Richard O'Keefe:
It would really REALLY **REALLY** help if we knew what
the heck you were trying to do.  There is an excellent
chance that it is MUCH simpler than you think.  If you
cannot show us the Smalltalk version of the problem,
can you show us the version for some other language?


On Sun, 7 Apr 2019 at 20:15, Roelof Wobben <[hidden email]> wrote:
Op 6-4-2019 om 15:15 schreef K K Subbu:
> On 06/04/19 4:49 PM, Roelof Wobben wrote:
>> Hello,
>>
>> I just learned double dispatch.
>> And now for the Robot challenge of exercism Tim has pointed me to
>> this
>> article(https://blog.metaobject.com/2019/04/accessors-have-message-obsession.html)
>>
>> but I fail to see how the move method looks like in that article.
>> I had a conversation with Tim in the exercism channel and the way he
>> explains it, it looks like double dispatch for me.
>>
>> Am I on the right track or do I oversee something here.
> unary methods like moveRight perform specific ops and are not
> parametric, so only a single dispatch, depending on the receiver, is
> needed.
>
> If you change it to move: aDistanceOrAngle, then performing requests
> like "move: 3 cms" or "move: 30 degrees" will depend not only on the
> receiver but also on the class of the argument. This would need double
> dispatch (aka multiple polymorphism). The first dispatch would be
> based on the receiver and the receiver's method would then dispatch it
> based on the class of the argument (i.e. Distance>>move or Angle>>move )
>
> HTH .. Subbu
>
>


hmm, still stuck

I have now a class Direction with as instance variables north, south,
east, west
and made the accessors.

then I thought I need a initialize like this :

initialize
    north = Direction( 0, -1).
    east  = Direction( 1,  0).
    south = Direction( 0,  1).
    west  = Direction(-1,  0).

but the Direction (0,-1)  is a problem . the compiler does not like the
(0,-1) part

to give you the big picture. I have a Robot which can turnRight ,
turnLeft and moveForward and I try to understand how the page would work
in my case.

So I have a object Direction as described above and a Object MoveForward
which is a subobject of Direction.
MoveForward has only 1 method :

IsMove
    ^  'A'

Roelof








VAST-browser-on-Class.png (30K) Download Attachment
VW-browser-on-String.png (63K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [OT] (slightly) What makes other dialects "enjoyable" for you? (WAS: difference between double dispatch...)

jtuchel
Richard,


as a comment on VA Smalltalk: you are referring to the Linux version,
which is also your Screenshot. I agree that Linux version of VA
Smalltalk is far from what I'd call enjoyable or beautiful.
If you take a look at the Windows version of VAST, you'll immediately
realize that Linux users miss a lot of nice stuff that has been added to
VA Smalltalk over the last few releases.

The reason here is most likely that commercial Smalltalk projects mainly
target Windows. This has changed for headless servers and I think it is
slowly starting to happen for GUI clients, but it will take some time.
We developers like Linux, but most of our end users still live and
breathe on Windows. Not that I like it, but it's a fact, at least in the
commercial field.

Joachim







Reply | Threaded
Open this post in threaded view
|

Re: [OT] (slightly) What makes other dialects "enjoyable" for you? (WAS: difference between double dispatch...)

Mariano Martinez Peck


On Wed, Apr 10, 2019 at 6:21 AM [hidden email] <[hidden email]> wrote:
Richard,


as a comment on VA Smalltalk: you are referring to the Linux version,
which is also your Screenshot. I agree that Linux version of VA
Smalltalk is far from what I'd call enjoyable or beautiful.
If you take a look at the Windows version of VAST, you'll immediately
realize that Linux users miss a lot of nice stuff that has been added to
VA Smalltalk over the last few releases.

The reason here is most likely that commercial Smalltalk projects mainly
target Windows. This has changed for headless servers and I think it is
slowly starting to happen for GUI clients, but it will take some time.
We developers like Linux, but most of our end users still live and
breathe on Windows. Not that I like it, but it's a fact, at least in the
commercial field.

Joachim

Hi,

I was about to answer same thing. Differently than Pharo, VW etc where the rendering happens inside Smalltalk, in VA Smalltalk, its native. And so, there are differences in the implementations between Unix and Windows. The version in Windows is "much less ugly" than the one on Linux. In the upcoming release we have also added HiDPI, as you can see here: https://twitter.com/MartinezPeck/status/1063432539908571136
And in 9.1, we did fix many UI related things for the Linux flavor, included the shortcuts. If you are still facing issues, please contact us separately (not to spam Pharo list)
 
Best, 

--
Mariano Martinez Peck
Reply | Threaded
Open this post in threaded view
|

Re: [OT] (slightly) What makes other dialects "enjoyable" for you? (WAS: difference between double dispatch...)

EstebanLM
Yeah, ok. 
But please do not take this thread as a way to defend/criticise other dialects (we all know there is a lot of passion around).
My intention is more to ask “what is cool of dialect X that you would like Pharo to have too” or “how can we make Pharo to be as beautiful and/or enjoyable as you think other dialects are”.

I know this is a very opinionated area. 
I believe Pharo looks and feels better than most of the other dialects, but I’m not stupid, I know there are tons of things to improve and I know there are good ideas/implementations around and I want watch them, to see how can improve even more our Pharo.

So please, stay cool :P

Esteban

On 10 Apr 2019, at 14:06, Mariano Martinez Peck <[hidden email]> wrote:



On Wed, Apr 10, 2019 at 6:21 AM [hidden email] <[hidden email]> wrote:
Richard,


as a comment on VA Smalltalk: you are referring to the Linux version,
which is also your Screenshot. I agree that Linux version of VA
Smalltalk is far from what I'd call enjoyable or beautiful.
If you take a look at the Windows version of VAST, you'll immediately
realize that Linux users miss a lot of nice stuff that has been added to
VA Smalltalk over the last few releases.

The reason here is most likely that commercial Smalltalk projects mainly
target Windows. This has changed for headless servers and I think it is
slowly starting to happen for GUI clients, but it will take some time.
We developers like Linux, but most of our end users still live and
breathe on Windows. Not that I like it, but it's a fact, at least in the
commercial field.

Joachim

Hi,

I was about to answer same thing. Differently than Pharo, VW etc where the rendering happens inside Smalltalk, in VA Smalltalk, its native. And so, there are differences in the implementations between Unix and Windows. The version in Windows is "much less ugly" than the one on Linux. In the upcoming release we have also added HiDPI, as you can see here: https://twitter.com/MartinezPeck/status/1063432539908571136
And in 9.1, we did fix many UI related things for the Linux flavor, included the shortcuts. If you are still facing issues, please contact us separately (not to spam Pharo list)
 
Best, 

--
Mariano Martinez Peck

12