A Live Developer Journal

Pharo Syntax notes

Notes are heavily based on Learning Object-Oriented Programming, Design and TDD with Pharo

Pharo is a pure object-oriented language based on Smalltalk, the original object-oriented language developed by Alan Kay in the 1970s.

Key facts about Pharo

Reserved Words
nil the undefined object
true, false boolean objects
self the receiver of the current message
super the receiver, in the superclass context
thisContext the current invocation on the call stack

Reserved syntactic constructs
"comment" comment
'string' string
#symbol unique string
$a, Character space The character a and a space
12 2r1100 16rc 12 (decimal, binary, hexidecimal)
3.14 1.2e3 floating point numbers
#(abc 123) literal array with the symbol #abc and the number 123
{foo . 3 + 2} dynamic array built from two expressions
#[123 21 255] byte array
expl. exp2 expression seperator (period)
; message cascade (semicolon)
var := expression assignment
^ expr return a result from a method (caret)
[ :e | expr] code block with a parameter
| var1 var2 | declaration of two temporary variables

Messages

Message syntax in Pharo is minimalist, but are central to all computations in Pharo. There are three kinds of messages: unary, binary and keyword-based messages.

In Pharo, messages are sent to a reciever, which is always to the left of the message. You can send different messages by using different message names (known as selectors.

'hello' reversed

In the code sample above, the message 'reversed' is sent to a reciever, which is a string object containing the characters 'hello'. The output of this message sending is a new string with the same characters in reverse order.

Unary messages

Unary messages involve only one object (their reciever), and do not take any arguments.

Keyword messages

Keyword messages pass arguments to the reciever.

'hello' at: 2

The above keyword message sends the message 'at:' to the reciever string object along with an argument, which is an integer object. The reciever object then returns the character object at position 2.

Keyword messages end with a colon to signify that it should be followed by an argument.

Keyword messages can pass more than one argument. A single message can have as many colon terminated keywords as necessary, each followed by an argument.

'hello' copyFrom: 1 to: 3

In the example above, the keyword message is 'copyFrom: to:'. The output of the code is the first three characters of the reciever string object.

Binary Messages

Binary messages can only be composed of symbols. They only expect a single argument, even though they do not end in a colon.

1 + 2

The main use of binary messages is for arithmetic operations like in the example above where we have sent the message '+' to an integer with '2' as an argument. The code above returns an integer object with the value of 3.

'Hello' , ' World!'

There are some examples outside of arithmetic operations that use binary messages. In the example above, we sent the ',' message with the argument ' World!' to the reciever string object. The ',' message represents a string cocatenation message.

Order of message execution

Unary >> Binary >> Keyword

Sending multiple messages to the same object

To send multiple messages to the same object in quick succession, we can group messages into a message cascade, which are seperated with semi-colons. When we do this, we only state the reciever once at the beginning of the cascade.


| aStream |
aStream := (String new: 100) writeStream.
aStream
  nextPutAll: 'Today, ';
  nextPutAll: Date today printString;
  contents

In the example above we have created a variable called 'aStream' which is assigned a new String object that is passed a message selector called 'writeStream'. Then we send the 'aStream' reciever a series of three messages. The first two messages are keyword messages 'nextPutAll:', 'nextPutAll:' and a unary message 'contents'. The output of this cascade of messages is 'Today, 28 January 2017'.

The cascade as a whole returns the value of it's last message. If the last message you send to the reciever is a selector called 'yourself', then it will return the object the messages were sent to. This is necessary when assigning elements to an array so that you get the array back and not just the last element that was assigned to it.

Objects / Classes

In Pharo, everything is an object and computation happens by sending messages to objects. Objects are created by sending messages to particular objects named classes, which define the structure and behaviour of the objects they create, also known as their instances.

We can create a new object by sending a message to the class itself.

String new

In the example above, we are creating a new string object by sending the unary message 'new' to the reciever which is a 'String' class.

Literal Objects

Some objects can be expressed directly in the code without having to use the 'new' message.

#(1 2 3)

In the example above, we have created an Array object containing three integer objects '1, 2' and '3'.

Variables

Variables are declared by writing their names between two pipe lines.


  | myKittensNameIs |
  myKittensNameIs := 'Berry'.

Variables can be assigned using the ':=' assignment statement. Statements are seperated by a full-stop, which makes Pharo code look like English sentences.

Blocks

Blocks (also known as lexical closures) are pieces of code to be executed later on. A block of code is surrounded by [ square brackets ].

Blocks represent anonymous methods that can be sorted, passed as arguments and axecuded on demand using the message selector 'value', which we see in the following code:


| adder |
adder := [ :x | x + 1 ].
adder value: 100

I the code above, we create a variable called 'adder' and assign it a block which accepts a value which is stored in a variable we can give any name on the left hand side of the pipe. In the right hand pipe we are passing the message '+' to the reciever 'x' with an argument of '1'. This block add one to it's reciever that we pass in with the value message that accepts the argument which then becomes the reciever in the block.

Control Structures

Blocks are used to express all control structures like conditionals and loops.


  n := 1.
  10 timesRepeat: [ n := n * 2 ].
  n

In the code above, we are multiplying by 2 a number 10 times.

Conditionals are expressed by sending one of the following messages: 'ifTrue:', 'ifFalse:'. 'ifTrue:ifFalse:', or 'ifFalse:ifTrue:' to the result of a boolean expression.


  (17 * 13 > 220)
    ifTrue: [ 'bigger' ]
    ifFalse: [ 'smaller' ]

The boolean expression at the start of the code above returns true or false depending on whether 17 * 13 is greater that 220. The message 'ifTrue:ifFalse' executes a different block of code depending on the result. If the result was true then in returns the string object 'bigger', otherwise it returns the string object 'smaller'.


  allSatisfy: aBlock
    self do: [:each | aBlock value: each) ifFalse: [^ false]].
    ^ true

In the code above, we have created a block message which returns false if one item in the block passed in as the reciever contains a value of 'false'. Otherwise it returns 'true'.


  | count |
  count := 0.
  do: [:c | c == $i ifTrue: [count := count + 1]].
  count

The code above counts all of the 'i' characters in a givin string.


  #(2 4 6 8 16 32) allSatisfy: [ :each | each even ]

The code above returns true if every element in the array is 'even'.

In the last couple of examplse, we used a message called 'allSatisfy', which is one of many super powerful behaviours implemented in the Collection class. This one is known as an iterator. We defined this message in the first of the last two code examples.

[ condition ] whileTrue; [ action. anotherAction]

The above code is an example of how we would write a while loop in Pharo.

Files and streams


  work := FileSystem disk workingDirectory.
  stream : (work / 'foo.txt') writeStream.
  stream nextPutAll: 'Hello World'.
  stream close.
  stream : (work / 'foo.txt') readStream.
  stream contents.
  >>> 'Hello World'
  stream close.

With just the syntax outlined above, we can understand 95% of Pharo.

Further video resources

Syntax Challenges

Message Identification

For each of the messages below, identify the reciever object, the message selector, the argument/s and the result returned by the expression execution.

3 + 4 Date today #('' 'World') at: 1 put: 'Hello' #(1 22 333) at: 2 #(2 33 -4 67) collect: [:each | each abs] 20 @ 50 SmallInteger maxVal #(a b c d e f) includesAll #(f d b) true | false Point selectors

Literal Objects

What kind of object does the following literal expressions return?

1.3 #node1 #(2 33 4) 'Hello, Berry' [ :each | each scale: 1.5 ] $A true 1

Kind of messages

Are the following messages unary, binary or keyword-based?

1 log Browser open 2 raisedTo: 5 10@20 point1 x point1 distanceFrom: point2

Results

What is the value returned by the executions of the following expressions?

1 + 3 negated 1 + (3 negated) 2 raisedTo: 3 + 2

  | anArray |
  anArray := #('first' 'second' 'third' 'fourth').
  anArray at: 2

#(2 3 -10 3) collect: [:each | each * each] 6 + 4 / 2 2 negated raisedTo: 3 + 2 #(a b c d e f) includesAll: #(f d b)

Unneeded parentheses

Rewrite the following expressions using the least number of parentheses.

x between: (pt1 x) and: (pt2 y)

  (x isZero)
    ifTrue: [...]
  (x includes: y)
    ifTrue: [...]


  (OrderedCollection new)
    add: 56;
    add: 33;
    yourself


((3 + 4) + (2 * 2) + (2 * 3))
(Integer primesUpTo: 64) sum
('http://www.pharo.org' asUrl) retrieveContents