Pharo

From Bauman National Library
This page was last modified on 1 June 2016, at 17:05.
Pharo
Pharo.png
Paradigm object-oriented
Designed by Pharo Board
First appeared 2008
Stable release 4.0 / 16 april 2015
Typing discipline dynamic, unary
License MIT, partially Apache 2.0
Website http://pharo.org/
Influenced by
Smalltalk, Squeak

Pharo is an open source implementation of the programming language and environment Smalltalk. Pharo offers strong live programming features such as immediate object manipulation, live update, hot recompilation. Live programming environment is in the heart of the system. Pharos is a Greek word (Φάρος) which means lighthouse. The Pharo logo shows a drawing of a lighthouse inside the final letter O of the name.

Pharo Zen

Pharo values and convictions are condensed in this simple list.

  • Easy to understand, easy to learn from, easy to change.
  • Objects all the way down.
  • Examples to learn from.
  • Fully dynamic and malleable.
  • Beauty in the code, beauty in the comments.
  • Simplicity is the ultimate elegance.
  • Better a set of small polymorphic classes than a large ugly one.
  • Classes structure our vocabulary.
  • Messages are our vocabulary.
  • Polymorphism is our esperanto.
  • Abstraction and composition are our friends.
  • Tests are important but can be changed.
  • Explicit is better than implicit.
  • Magic only at the right place.
  • One step at a time.
  • There is no unimportant fix.
  • Learning from mistakes.
  • Perfection can kill movement.
  • Quality is a emerging property.
  • Simple processes to support progress.
  • Communication is key.
  • A system with robust abstractions that a single person can understand.

Syntax in a nutshell

Pharo, like most modern Smalltalk dialects, adopts a syntax very close to that of Smalltalk-80. The syntax is designed so that program text can be read aloud as though it were a kind of pidgin English:

(Smalltalk includes: Class) ifTrue: [ Transcript show: Class superclass ]

Pharo’s syntax is minimal. Essentially there is syntax only for sending messages (i.e., expressions) . Expressions are built up from a very small number of primitive elements. There are only 6 keywords, and there is no syntax for control structures or declaring new classes. Instead, nearly everything is achieved by sending messages to objects. For instance, instead of an if-then-else control structure, Smalltalk sends messages like ifTrue: to Boolean objects. New (sub-)classes are created by sending a message to their superclass.

Syntactic elements

Expressions are composed of the following building blocks:

  1. six reserved keywords, or pseudo-variables: self, super, nil, true, false, and thisContext;
  2. constant expressions for literal objects including numbers, characters, strings, symbols and arrays;
  3. variable declarations;
  4. assignments;
  5. block closures;
  6. messages.

We can see examples of the various syntactic elements in table:

Syntax What it represents
startPoint a variable name
Transcript a global variable name
self pseudo-variable
1 decimal integer
2r101 binary integer
1.5 floating point number
2.4e7 exponential notation
$a the character ‘a’
’Hello’ the string “Hello”
#Hello the symbol #Hello
#(1 2 3) a literal array
{1. 2. 1+2} a dynamic array
"a comment" a comment
x := 1 assign 1 to x
[ x + y ] a block that evaluates to x+y
<primitive: 1> virtual machine primitive or annotation
3 factorial unary message
3+4 binary messages
2 raisedTo: 6 modulo: 10 keyword message
↑true return the value true
Transcript show: ’hello’. Transcript cr expression separator (.)
Transcript show: ’hello’; cr message cascade (;)

Local variables startPoint is a variable name, or identifier. By convention, identifiers are composed of words in “camelCase” (i.e., each word except the first starting with an upper case letter). The first letter of an instance variable, method or block argument, or temporary variable must be lower case. This indicates to the reader that the variable has a private scope.

Shared variables Identifiers that start with upper case letters are global variables, class variables, pool dictionaries or class names. Transcript is a global variable, an instance of the class TranscriptStream.

The receiver. self is a keyword that refers to the object inside which the current method is executing. We call it “the receiver” because this object will normally have received the message that caused the method to execute. self is called a “pseudo-variable” since we cannot assign to it.

Integers. In addition to ordinary decimal integers like 42, Pharo also provides a radix notation. 2r101 is 101 in radix 2 (i.e., binary), which is equal to decimal 5.

Floating point numbers can be specified with their base-ten exponent: 2.4e7 is 2.4 × 107.

Characters. A dollar sign introduces a literal character: $a is the literal for ‘a’. Instances of non-printing characters can be obtained by sending appropriately named messages to the Character class, such as Character space and Character tab.

Strings. Single quotes are used to define a literal string. If you want a string with a quote inside, just double the quote.

Symbols are like Strings, in that they contain a sequence of characters. However, unlike a string, a literal symbol is guaranteed to be globally unique. There is only one Symbol object #Hello but there may be multiple String objects with the value 'Hello'.

Compile-time arrays are defined by #( ), surrounding space-separated literals. Everything within the parentheses must be a compile-time constant. For example, #(27 (true false) abc) is a literal array of three elements: the integer 27, the compile-time array containing the two booleans, and the symbol #abc. (Note that this is the same as #(27 #(true false) #abc).)

Run-time arrays. Curly braces { } define a (dynamic) array at run-time. Elements are expressions separated by periods. So { 1. 2. 1+2 } defines an array with elements 1, 2, and the result of evaluating 1+2. (The curlybrace notation is peculiar to the Pharo and Squeak dialects of Smalltalk! In other Smalltalks you must build up dynamic arrays explicitly.)

Comments are enclosed in double quotes. "hello" is a comment, not a string, and is ignored by the Pharo compiler. Comments may span multiple lines.

Local variable definitions. Vertical bars | | enclose the declaration of one or more local variables in a method (and also in a block). Assignment. := assigns an object to a variable.

Blocks. Square brackets [ ] define a block, also known as a block closure or a lexical closure, which is a first-class object representing a function. As we shall see, blocks may take arguments and can have local variables.

Primitives. <primitive: ...> denotes an invocation of a virtual machine primitive. (<primitive: 1> is the VM primitive for SmallInteger»+.) Any code following the primitive is executed only if the primitive fails. The same syntax is also used for method annotations.

Unary messages consist of a single word (like factorial) sent to a receiver (like 3).

Binary messages are operators (like +) sent to a receiver and taking a single argument. In 3+4, the receiver is 3 and the argument is 4.

Keyword messages consist of multiple keywords (like raisedTo:modulo:), each ending with a colon and taking a single argument. In the expression 2 raisedTo: 6 modulo: 10, the message selector raisedTo:modulo: takes the two arguments 6 and 10, one following each colon. We send the message to the receiver 2.

Method return. " is used to return a value from a method. (You must type ^ to obtain the " character.)

Sequences of statements. A period or full-stop (.) is the statement separator. Putting a period between two expressions turns them into independent statements.

Cascades. Semicolons can be used to send a cascade of messages to a single receiver. In Transcript show: 'hello'; cr we first send the keyword message show: 'hello' to the receiver Transcript, and then we send the unary message cr to the same receiver.

Pseudo-variables

In Smalltalk, there are 6 reserved keywords, or pseudo-variables:

  1. nil,
  2. true,
  3. false,
  4. self,
  5. super,
  6. thisContext.They are called pseudo-variables because they are predefined and cannot be assigned to. true, false, and nil are constants while the values of self, super, and thisContext vary dynamically as code is executed.
  • true and false are the unique instances of the Boolean classes True and False.
  • self always refers to the receiver of the currently executing method.
  • super also refers to the receiver of the current method, but when you send a message to super, the method-lookup changes so that it starts from the superclass of the class containing the method that uses super.
  • nil is the undefined object. It is the unique instance of the class UndefinedObject. Instance variables, class variables and local variables are initialized to nil.
  • thisContext is a pseudo-variable that represents the top frame of the runtime stack. In other words, it represents the currently executing MethodContext or BlockClosure. thisContext is normally not of interest to most programmers, but it is essential for implementing development tools like the debugger and it is also used to implement exception handling and continuations.

Message sends

There are three kinds of messages in Pharo.

  1. Unary messages take no argument. 1 factorial sends the message factorial to the object 1.
  2. Binary messages take exactly one argument. 1 + 2 sends the message + with argument 2 to the object 1.
  3. Keyword messages take an arbitrary number of arguments. 2 raisedTo: 6 modulo: 10 sends the message consisting of the message selector raisedTo:modulo: and the arguments 6 and 10 to the object 2.

Unary message selectors consist of alphanumeric characters, and start with a lower case letter. Binary message selectors consist of one or more characters from the following set:

+ − / \ * ~ < > = @ % | &amp; ? ,

Keyword message selectors consist of a series of alphanumeric keywords, where each keyword starts with a lower-case letter and ends with a colon.

Unary messages have the highest precedence, then binary messages, and finally keyword messages, so:

2 raisedTo: 1 + 3 factorial → 128

(First we send factorial to 3, then we send + 6 to 1, and finally we send raisedTo: 7 to 2.) Recall that we use the notation expression −! result to show the result of evaluating an expression.

Precedence aside, evaluation is strictly from left to right, so not 7.

1 + 2 * 3 → 9

Parentheses must be used to alter the order of evaluation:

1 + (2 * 3) → 7

Message sends may be composed with periods and semi-colons. A period separated sequence of expressions causes each expression in the series to be evaluated as a statement, one after the other.

Transcript cr.
Transcript show: 'hello world'.
Transcript cr

This will send cr to the Transcript object, then send it show: 'hello world', and finally send it another cr.

When a series of messages is being sent to the same receiver, then this can be expressed more succinctly as a cascade. The receiver is specified just once, and the sequence of messages is separated by semi-colons:

Transcript cr;
show: 'hello world';
cr

This has precisely the same effect as the previous example.

Example

There is an example of a simple game: Lights Out.

"[1] Create class #LOCell"
SimpleSwitchMorph subclass: #LOCell
	instanceVariableNames: 'mouseAction'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'PBE-LightsOut'

"[2] Initializing instances of LOCell"
initialize
	super initialize.
	self label: ''.
	self borderWidth: 2.
	bounds := 0@0 corner: 16@16.
	offColor := Color paleYellow.
	onColor := Color paleBlue darker.
	self useSquareCorners.
	self turnOff

"[3] A typical setter method"
mouseAction: aBlock
	↑mouseAction := aBlock

mouseMove: anEvent
	"Do nothing"

"[4] An event handler"
mouseUp: anEvent
	mouseAction value

"[5] Defining the LOGame class"
BorderedMorph subclass: #LOGame
	instanceVariableNames: 'cells'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'PBE-LightsOut'!

"[6] A constant method."
cellsPerSide
	"The number of cells along each side of the game"
	↑10

"[7] Initializing the game"
initialize
	| sampleCell width height n|
	super initialize.
	n := self cellsPerSide.
	sampleCell := LOCell new.
	width := sampleCell width.
	height := sampleCell height.
	self bounds: (5@5 extent: ((width * n) @ (height * n)) + (2 * self borderWidth)).
	cells := Matrix new: n tabulate: [ :i :j | self newCellAt: i at: j ].

"[8] An initialization helper method"
newCellAt: i at: j
	"Create a cell for position (i,j) and add it to my on-screen
	representation at the appropriate screen position. Answer the new cell"

	| cell origin |

	cell := LOCell new.
	origin := self innerBounds origin.
	self addMorph: cell.
	cell position: ((i - 1) * cell width) @ ((j - 1) * cell height) + origin.
	cell mouseAction: [self toggleNeighboursOfCellAt: i at: j].
	^ cell! !
        ↑c

"[9] The callback method"
toggleNeighboursOfCellAt: i at: j
	(i > 1) 				    ifTrue: [ (cells at: i - 1 at: j) toggleState ].
	(i < self cellsPerSide)  ifTrue: [ (cells at: i + 1 at: j) toggleState ].
	(j > 1)                         ifTrue: [ (cells at: i at: j - 1) toggleState ].
	(j < self cellsPerSide) ifTrue: [ (cells at: i at: j + 1) toggleState ].
  1. Create class #LOCell. This new definition consists of a Smalltalk expression that sends a message to the existing class SimpleSwitchMorph, asking it to create a subclass called LOCell. (Actually, since LOCell does not exist yet, we passed as an argument the symbol #LOCell which stands for the name of the class to create.) We also tell it that instances of the new class should have a mouseAction instance variable, which we will use to define what action the cell hould take if the mouse should click over it.
  2. Initializing instances of LOCell. The first thing that this method does is to execute the initialize method of its superclass, SimpleSwitchMorph. The idea here is that any inherited state will be properly initialized by the initialize method of the superclass. It is always a good idea to initialize inherited state by sending super initialize before doing anything else; we don’t know exactly what SimpleSwitchMorph’s initialize method will do, and we don’t care, but it’s a fair bet that it will set up some instance variables to hold reasonable default values, so we had better call it, or we risk starting in an unclean state. The rest of the method sets up the state of this object. Sending self label: ' ', for example, sets the label of this object to the empty string. The expression 0@0 corner: 16@16 probably needs some explanation. 0@0 represents a Point object with x and y coordinates both set to 0. In fact, 0 @0 sends the message @ to the number 0 with argument 0. The effect will be that the number 0 will ask the Point class to create a new instance with coordinates (0,0). Now we send this newly created point the message corner: 16@16, which causes it to create a Rectangle with corners 0@0 and 16@16. This newly created rectangle will be assigned to the bounds variable, inherited from the superclass. Note that the origin of the Pharo screen is the top left, and the y coordinate
  3. A typical setter method. Method does nothing more than set the cell’s mouseAction variable to the argument, and then answers the new value. Any method that changes the value of an instance variable in this way is called a setter method; a method that answers the current value of an instance variable is called a getter method.
  4. An event handler. What this method does is to send the message value to the object stored in the instance variable mouseAction.
  5. Defining the LOGame class. Here we subclass BorderedMorph; Morph is the superclass of all of the graphical shapes in Pharo, and (surprise!) a BorderedMorph is a Morph with a border.
  6. A constant method. This method could hardly be simpler: it answers the constant 10. One advantage of representing constants as methods is that if the program evolves so that the constant then depends on some other features, the method can be changed to calculate this value.
  7. Initializing the game. The line | sampleCell width height n | declares 4 temporary variables. The Smalltalk self cellsPerSide sends the message cellsPerSide to self, i.e., to this very object. The response, which will be the number of cells per side of the game board, is assigned to n. Then we create new LOCell object. After that we sets the bounds of the new object. The last line sets the LOGame object’s instance variable cells to a newly created Matrix with the right number of rows and columns. Matrix new: n tabulate: [ :i :j | self newCellAt: i at: j ] creates a new n×n matrix and initializes its elements. The initial value of each element will depend on its coordinates. The (i,j)-th element will be initialized to the result of evaluating self newCellAt: i at: j.
  8. An initialization helper method. Method answers a new LOCell, specialized to position (i, j) in the Matrix of cells. The last line defines the new cell’s mouseAction to be the block [self toggleNeighboursOfCellAt: i at: j ]. In effect, this defines the callback behaviour to perform when the mouse is clicked.
  9. The callback method. Method toggles the state of the four cells to the north, south, west and east of cell (i, j). The only complication is that the board is finite, so we have to make sure that a neighboring cell exists before we toggle its state.

Reference

  1. Pharo
  2. Pharo vision document
  3. Pharo by example

The article is written by Zheludkov A.V.