Saturday, 31 October 2015

Domain model, Presenter, View

Domain model classes should be subclasses of Model and not of Object.  This means they inherit the event methods.

Then we need a presenter equivalent to the app-level part of the domain model - i.e. that contains the presenter for the first Class the app domain model has.
In my app, a presenter that displays a DraftPool as its model and allows for its various aspects to be edited. This should be a top level shell window so a suitable superclass for our new class will be Shell.

DraftPool subclass: #Model
instanceVariableNames: 'name poolId initialInd cumInd bouts'


Shell subclass: #DraftPoolShell
       'namePresenter poolIdPresenter initialIndPresenter boutsPresenter cumIndPresenter'

For every instance variable in the model, the shell includes an instance variable for the matching presenter.  These instance variables with the suffix 'Presenter's will handle the editing of each of the instance variables in the model.  They are set to be a specific subPresenter for the type of data in the model's instance varable in the Shells's createComponents method.

So the DraftFencing's app has top level object is the DraftPool, with these instance variables:
strings: name poolId 
numbers: initialInd cumInd
lists of pointers to lower level objects: bouts

Moving to presenters,

The Shell is is a DraftPoolShell, and it has instance variables:
textPresenter: namePresenter poolIdPresenter
numberPresenter: initialIndPresenter cumIndPresenter
listPresenter: boutsPresenter

( n.b. NumberPresenter are for integers or reals )

       "Private - Create the presenters contained by the receiver"

       super createComponents.
       namePresenter := self add: TextPresenter new name: 'name'.
       poolIdPresenter := self add: TextPresenter new name: 'poolId'.
       initialIndPresenter := self add: NumberPresenter new name: 'initialInd'.
       boutsPresenter := self add: ListPresenter new name: 'bouts'.
       cumIndPresenter := self add: NumberPresenter new name: 'cumInd'.

i.e. for each aspect of the domain model (the instance variables of the domain model) we create a suitable presenter, and add it to the shell composite. 

The choice of presenter to use for each aspect depends on the effective type of the aspect's value

The sub-presenters are given names to identify them when the view is connected and each sub-view will be given an identical name so that presenter-view pairs can be matched and connected together. 

Then we specify how to connect the model to the presenter, as a method of the Shell of the top-level Domain model object.

model: aDraftPool
"Set the model associated with the receiver."
super model: aDraftPool.
"Presenters for aspects which automatically trigger on changes" 
namePresenter model: (aDraftPool aspectValue: #name).
poolIdPresenter model: (aDraftPool aspectValue: #poolId).
initialIndPresenter model: (aDraftPool aspectValue: #initialInd).
boutsPresenter model: (aDraftPool bouts).
cumIndPresenter model: (aDraftPool aspectValue: #cumInd).

"Presenters for parts of a model which need to trigger their own events on changes"
cumIndPresenter model aspectTriggers: #cumIndChanged.

If a model triggers its own events when some aspects of it are changed,  we must explicitly state this, 
so when such an aspect is changed, i.e. other than by going through the adaptor itself,
the adaptor can to update its observers.

In DraftPool, the only aspect that triggers a change this way is #cumInd. 
We inform our newly created aspect adaptor that its model
triggers #cumIndChanged whenever the cumInd is updated. See

1st: we assign the DraftPool as the actual model of our composite using the super message send. 

2nd: Then we set the model of each sub-presenter to be ValueAspectAdaptors on the appropriate aspects of the Pool. 

Tip: a shortcut to creating a ValueAspectAdaptor is to send #aspectValue: to the account object specifying the name of the aspect you want.

3rd: set the special cases: The one special case here; creating the ValueAspectAdapator for #cumInd. Once created the adaptor is sent #aspectTriggers:. This is important since it informs the adaptor that the aspect it's connected to will trigger its own update notifications whenever it is changed ( remember the #cumInd method for DraftPool? )
cumInd: anInteger
     cumInd := aInteger.
     self trigger: #cumIndChanged

i.e. it triggers a #cumIndChanged event when the cumulative indicators is assigned to using #cumInd: directly. If we didn't send #aspectTriggers: to the ValueAspectAdaptor we create for this aspect (see above)  then it wouldn't be able to inform its observers when the #cumInd: method is called by some other object. This would result in some notifications being missed and the displays of cumulative indicator  not being updated correctly.

4th:  wire together the sub-presenters, using the standard Smalltalk event notification mechanism. This wiring is implemented by overriding the createSchematicWiring method. You only need to override this method if you wish to respond to events triggered by your sub-presenters. 

In this case we want to send an #editTransaction message to the DraftPoolShell when a transaction in the transactions list is double-clicked.

       "(A Class method) Private - Create the trigger wiring for the receiver"

       super createSchematicWiring.
       boutsPresenter when: #actionRequested send: #editTransaction to: self.

6th, when creating any presenter,: define class methods, defaultModel and defaultView

defaultModel should answer an object which the presenter will use as its model in cases where this is not specified explicitly (which is the usual situation). In this case we answer a default instance of DraftPool. 

defaultView must answer a resource name to use to load a default view. The defaultView method inherited from Presenter class specifies a view name, not surprisingly, of 'Default view'. This is suitable for most purposes. We just have to make sure that this matches the name under which the actual view is saved by the View Composer. 

       "(A class method) Answer a default model to be assigned to the receiver when it is initialized."

       ^DraftPool new 

By default any command that is implemented by a presenter will be enabled when the menu is pulled down. To change this default behavior and enable or disable commands directly, you must implement a queryCommand: method.

Creating a Date from a date-formatted string

Date readFrom: '04/02/2013' readStream pattern: 'dd/mm/yyyy'


Iterators - OO Computing with Smalltalk, Ch23.5

If we wish to perform the same operation on all the elements in a collection at once, we use an enumerating message.

e.g. do: or detect:ifNone:

e.g. myCollection do: [ :each | each doSomething ]

do: is an example of a generalised iterator

a do: block will reference all the member elements of a Set when used on a set, and all the member elements of a Dictionary when used on a dictionary.

e.g. x := mySet detect: [ :each | each = 34 ] ifNone: [ nil ]

x is true if there is a member of the set which is 34, and returns nil if there is no matching member

In a dictionary, detect:ifNone: would check values, not keys.

Basic UI items

Dialog warn: 'a warning message'

Suggested Pharo equivalent:

UIManager default alert: 'a warning message'

Friday, 30 October 2015

Blocks - OO Computing with Smalltalk Ch 23.4

A Block object is created by enclosing any expression, or expression series, in square brackets.

The block can be thought of being just like a method, but without a name.

Blocks can be assigned to variables.

A block can have arguments, just like a method, and they get called block arguments.

They are declared inside the block, at the start, with a preceding colon, and a | at the end of the list of block arguments.


    aBlock := [ :blockArgument1 :blockArgument2 | "and the meat of the block goes here." ]  

To evaluate a block with a single block argument, you send it the message value:
To evaluate a block with two block arguments, you send it the message value:value:
To evaluate a block with three block arguments, you send it the message value:value:value:

To evaluate a block with more than three arguments, you send it the message valueWithArguments: with the arguments in an array as the message argument.

Blocks can have temporary variables, declared between | | immediately after the block arguments are declared.

Methods return either self or the explicit ^ return,

Blocks return either the result of evaluating the last expression in the block, or the first explicit ^ return they encounter.


e.g. t

Sets and Dictionaries - OO Computing with Smalltalk - Ch 23 original notes

Objects which comprise a collection are referred to as elements

A collection may have elements which are not the same class as the other elements - but this is not common

Ordered and sorted
An ordered collection preserves the order in which the elements were added to the collection

A sorted collection has an ordering determined by a property of the element.

Only one element exists with a given value

For all collections:
add: and return: returns the argument, not the modified receiver

both have ifAbsent: variants - add:IfAbsent: and remove:ifAbsent:

ifAbsent: catches exceptions Or you can pre-qualify the add: by checking if the collection already includes: the element to be added

A key is a property that uniquely identifies an element.

An Association is a key value pair

When creating instances of Association, it's important to check that the key is unique.

A collection of Association objects

Create an association by
    anAssociation := Association key: 'keyOfElement' value: 'valueOfElement'
    anAssociation := ( 'keyOfElement' -> 'valueOfElement' )

A Dictionary is not allowed to remove one of its associations directly, so there are overridden inherited methods remove: and remove:ifAbsent: which cause an exception if used.

Instead, removeKey: aKey is used.

An includesKey: message checks for the existence of the key. includes:checks for the existence of a value.

at:ifAbsent: is needed - for what?

at:put: combines  anAssociation := Association key:value: with aDictionary add: anAssociation

if the key already exists in the dictionary, then the pre-existing value is over-written with the new value.

Collections notes


ALL collections understand
includes: , isEmpty and occurrencesOf: 
the enumeration messages do:select:reject: ,  detect:ifNone: , 
collect: (which is like lisp’s map),, inject:into: (which performs a left fold) and many more

add:  answers with its argument NOT with the amended collection.

A key uniquely identifies some object or row.

Bag - a Set where it will count the number of instances of each member ?
(An unordered collection of objects. i.e. a set with a memory. if the same object is added twice, it will report that the element has been stored twice.)

A Set provides an unordered collection of unique elements.  i.e. if you add a duplicate of an existing item, nothing happens.

IdentitySet (and IdentityDictionary) has integers or Symbols as the key values

Collections can be
sequenced - they proceed from first: to last:

indexed - they also respond to at: (aNumber or aSymbol)
i.e. they have an integer key

keyed: they have an index which is an i

unordered - i.e. they do not respond to

Arrays are of fixed size, and is indexed.

Indexed collections are ones which have an Integer number as their index - i.e. an integer will allow you to access a specific member.

There is a Matrix Class for multi-dimensional arrays

2D = table

3D = volume (GRL term)

There is also SparseLargeTable and its subClass SparseLargeArray

OrderedCollection and its subclass, SortedCollection

Which one is a Dictionary?  Neither.  Class Dictionary (and class SmallDictionary) are dictionaries.

Dictionaries are keyed to a string value.

Only one entry is allowed for each string value.

Dictionary - implementation details in Pharo 4

In Pharo, the elements in a dictionary are physically held in an array.

There is an elements property, and an array property, and a tally property.

The elements property is what we mentally model as the dictionary - a list of Association objects

The array, is a an array of the same Association objects, stored in an array.

This array is a fixed size, larger than the set of associations stored in the elements property.
Any time the array becomes full, it automatically creates a larger array so there are still free slots for future adds.

I'm assuming this is done for two reasons - it's faster to extract and copy a full array into a fresh, larger array than it is to incrementally increase a collection in size by one every time a new item is added.

I suspect also that the positioning of the elements in the array provides a b-tree index for searching for the elements.

Thursday, 29 October 2015

Monticello Repository - basic How-to steps

Category Gawain-Directoire

To get Monticello Browser to realise that a Smalltalk category and a Smalltalk package are the same, they must have identical names.

e.g. Gawain-Directoire

must use that exact name in the Package browser.

Monticello Repositories on SqueakSource cannot have non-alpha characters in their name.

But - the Monticello +Repository details in the Browser are the SqueakSource package name and UserID .  If you omit the password, it will be asked for later, and Monticello will add it to the Repository details automatically - in plain text - for future use.

When loaded, the new package will be at the bottom of the list of Packages, *nopt* in alphabetical order.

So - Call the +Package in Monticello by the same name as the Category name in the System Browser.

They will automatically be kept in step.

Wednesday, 28 October 2015

Pharo By Example, Chapter 5

variables are accessible only by the instance of the class
(in C++ and Java, all instances of the class share instance variables)

How to get Smalltalk to start a specific app on startup

How do I get Squeak to start running my game app as soon as the image is opened?

* Open the World menu.
* Click on the "do..." item
* Choose "edit this list"
* Add a line saying:
 Smalltalk saveSession. MyGameMorph openInWorld.
[Note this _needs_ to be a single line, so it might be easier to have a
method on the class side of your game called "saveAndStartup" which
you'd enter here].
* Accept the changes
* Click on the entry on the "do" menu.

Now your game comes up nicely. The reason why I (at times ;-) prefer
that approach is that by simply saving the image _without_ using the
above menu entry you get it back to the state where your game is not
starting up.

It's easy-

First, add a #shutDown method on the class-side of your class.
and then execute "Smalltalk addToStartUpList: YourClass."

There's also a #addToShutDownList: if you need a method called when the
image is shutting down.  Just throw on a #shutDown method on the class

Also of interest may be SystemDictionary>>#addToShutDownList:after:
and SystemDictionary>>#addToStartUpList:after: which do the same thing,
but allow you to have some order in which they occur.


A:</b> There are a few different ways to do this:
1. You may be able to just save your image in an interesting configuration, i.e. with your application open. Keep in mind that when the image starts, it starts <b>exactly</b> where it left off.
2. As a variation, you can save your image like this (do all this code together in one DoIt):
= Smalltalk snapshot: true andQuit: false.
= self inform: 'Hello, world.' "Replace this with your application-starting code"
3. You can use addToStartUpList: to register any object that understands startUp:; that object's startUp: method will be invoked every time the system starts. (Look in "snapshot and quit" under class SystemDictionary for details).
4. You can specify a script file when starting up Squeak, named on the command line. See *Writing scripts* for details.
5. . Open the *World menu*.
. Click on the "do..." item
. Choose "edit this list"
. Add a line saying:
Smalltalk saveSession. MyGameMorph openInWorld.
[Note this _needs_ to be a single line, so it might be easier to have a method on the class side of your game called "saveAndStartup" which you'd enter here].
. Accept the changes
. Click on the entry on the "do" menu.
Now your game comes up nicely. The reason why I (at times ;-) prefer that approach is that by simply saving the image _without_ using the above menu entry you get it back to the state where your game is not starting up.

Saturday, 24 October 2015

Pharo By Example Chapter 4

Everything is a message

(Except for syntax)

You cannot overload a message except with a message of the same kind (Unary, binary, keyword)

A message is the selector plus arguments (if there are arguments)

It is sent to a receiver
i.e. whatever is 'self' for the message

Binary messages only ever contain these characters:
+, −, * , /, &, =, >, |, <, ∼, and @

Precedence is always unary, then binary then keyword

Pharo By Example, Chapter 2

Create a Package by right-clicking on the Package (i.e. the top-left) pane of the Package Browser.

Where traditionally Smalltalk had "Categories" in the left-most pane - a agglomeration of related classes - Pharo has "Packages".  Packages  

A Package is a collection of related classes and extension methods that may be versioned using the Monticello versioning tool. By convention, package names and category names are the same.  The distinction is only a difference once you use the Monticello versioning tool.

Create a new Package's first class by typing over the pro-forma code in the codepane.

The first line is an instruction to the package to create a class that has the same name as the symbol string used as the keyword argument.

The instance variable names use actual strings.

The category argument comes pre-filled from the create package dialogue.

Right-click on the pane and "accept", or alt-s (which originally stood for 'save' rather than accept)


The Pharo screen has a co-ordinate system with 0,0 as the top-left.

The x-co-ord increases rightwards, and the y co-ord increases as you go downwards.


The bottom-right pane of the object Inspector window is a codepane.


In the protocols pane of the System Browser, protocols are called categories in the drop-down action menu.


You can drag and drop methods to new classes, methods to new protocols


Friday, 23 October 2015

Pharo By Example, Ch. 3

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).)

i.e. Compile-time arrays must contain unique objects - symbols or constants

Only run-time arrays can contain object instances like strings.

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.

How do you build up dynamic arrays explicitly?


Local variable definitions. Vertical bars | | enclose the declaration of one or more local variables in a method (and also in a block).


There are 6 reserved keywords, or pseudo-variables: nil, true, false, self, super, and thisContext.

You cannot assign a value to a pseudovariable.

self - the receiver of the currently executing method
super - the superclass of the receiver
(technically, the receiver, but doing method-lookup starting from the receiver's superclass)
nil - 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, Message sends 53 but it is essential for implementing development tools like the debugger and it is also used to implement exception handling and continuations.
true and false are the unique instances of the Boolean classes True and False.



Code contained between [ and ]

They return the value of the last evaluated expression. (unless there is an explicit return (with ^), in which case it does not answer a value)

Blocks may take parameters. Each parameter is declared with a leading colon.

A vertical bar separates the parameter declaration(s) from the body of the block.

For a block with one parameter, you must send it the message value: with one argument.
For a block with two parameters, you must send it the message value:value:, each with one argument. with two argument.
For a block with three parameters, you must send it the message value:value:value:, each with one argument. 
For a block with four parameters,  you must send it the message value:value:value:value:, each with one argument. 
For a block with more than four parameters, you must send it the message valueWithArguments: and pass the arguments in an array.

A block with a large array of parameters is often the sign of a design issue.

Blocks may also declare local variables, which are surrounded by vertical bars, just like local variable declarations in a method. Locals are declared after any arguments, e.g.
[ :x :y | | z | z := x+ y. z ] value: 1 value: 2
→ 3

Blocks are instances of the class BlockClosure. This means that they are objects, so they can be assigned to variables and passed as arguments just like any other object.


Loops and controls

control constructs are typically expressed by sending messages to booleans, numbers and collections, with blocks as arguments.

ifTrue: [  ]
ifFalse: [  ]

[ ] whileTrue: [ ]
[ ] whileFalse: [ ]

nn timesRepeat: [ ]


(nn to: nn) do: [ :x | result := result, x printString, ' ' ]

(nn: to nn2:) is a collection - a dynamic array
1 to 10 would give
result -> '1 2 3 4 5 6 7 8 9 10'

= has the same value as

== exactly equal to

(or else it's == and === )

[ n < 1000 ] whileTrue: [ n := n* 2 ].


builds a new collection of the same size, transforming each element.

(1 to: 10) collect: [ :each | each * each ]

-> #(1 4 9 16 25 36 49 64 81 100)

select: and reject:
build subset collections

 returns the first element matching the condition

Monday, 19 October 2015

Pharo By Example (PBE), Ch. 1

Pharo consists of 4  files.


<name>.sources   Essentially, libraries of code

pharo.image  A snapshot of the system at the last time it was closed gracefully

pharo.changes A list of all changes applied to Pharo since it was started on your installation

Theoretically, anyway.  The PBE 1.1 One-click installer has a plethora of files in the .zip


Moved Pharo to D:\

When it was in c:\Program Files (x86)\GRL\Coding\,
   a) it lacked permissions to save changes to the image file.
   b) pharo.exe was not recognised by the Start Menu

I have also now right-clicked on Pharo.exe and pinned it to the taskbar


"click" = left-click
"action-click" = right-click
"meta-click" = middle/wheel-click


Lots of the options are shown by left-clicking on the Pharo window background, e.g.
Workspace [open]
Class Browser
Monticello Browser
Tools submenu -
   Message name
   Method finder
   Transcript [open]
   File Browser
   Process Browser
   Recover lost changes
   Change sorter
      Image Browser
      + others
Windows submenu
Debug submenu
System submenu
Plus, Save & Quit


The Transcript is a display-only window
The Workspace allows user input, and also displays output


A protocol is a list of methods that an object will respond to.

If an object has a lot of methods, the methods will be grouped into several protocols, each protocol will contain a list of related methods.  e.g. Class Object has 59 protocols!


You can navigate to a class in the Class browser
      <classname> browse
      in a workspace, and do it
highlight its name and "browse it" alt-b
find it by clicking in the package pane, and alt-f

To find a method - World | Tools | Method Finder

In the top-left pane type a method's name OR an example of what it does
e.g. 'eureka' . 'EUREKA'
will offer the names of methods that do that

You can also find a method in the Method Finder


A Class will now often have a "category" defined, e.g. the OBCommand Class has a category of "OmniBrowser-Commands"

This means that it is part of the OmniBrowser package, in the Commands sub-category

Category and package seem to be interchangable terms.


By convention, tests for methods go in a Class named "<Classname>Test"


If you want to use a literal ' in Smalltalk, escape it with a preceding ' - so type '' in the code (2 ' side by side)


To run a test:  World | Test Runner

In the bottom-right pane, the Class and method of any failing tests is listed.

To bring up the Debugger from a test that's been run in the TestRunner, select the line of text in the bottom-right pane of the TestRunner, (of the form <ClassName> >> #<methodName>

The top-most message in the Debugger pane is the method that had the issue.


Saturday, 17 October 2015

Pharo By Example Issues

All the issues are occurring from
Pharo-1.0 Latest update: #10517
installed on Windows 7 from the PBE-OneClick zip
Pharo By Example version of 2009-10-28

1) Section 1.4
meta-click does not bring up the Morphic halo on the BouncingAtomsMorph
For screenshots, see How to kill the BouncingAtomsMorph

2) Section 1.6 p14
Inspect it does not bring up the "newInspector", just the inspector.

As opposed to what's shown in the book:
3) Section 1.6 p14
Explore it does not bring up the explorer shown in the book


Clicking on the triangle in Pharo only toggles the trianle from pointing down to pointing right.

PBE Ch. 2.2

Book tells you to click on "accept", where you actually click on "OK".

PBE Ch 2.3

The book talks of the code pane of the PackageBrowser being outlined in red when there are unsaved changes,  

In PBE-OneClick, there is a small orange gradient-filled triangle at the top-right of the code-pane.

It then talks of typing in a class comment, but it is not obvious where this should be typed in.  Other Packages and other Classes do not seem to have Class comments.

Ch. 2.4

In the object Inspector, we're told to enter self bounds: (200@200 corner: 250@250) and do it.

This results in either :
Message Not Understood: Rectangle>>bounds: in the debugger or 
or, without the parentheses, 
Message Not Understood: Array>>bounds:corner: in the debugger or 
Unknown selector  When bounds:corner: is selected in the Unknown Selector list of possible matches, it then provides bounds:corner:  MessageNot Understood by class SmallInt

No LightsOut! window appears on-screen


It does appear in Pharo 1.4 #14457

(Other than inspecting up the parent object hierarchy, nothing different was done) 

This does not work in either the download recommended for use with PBE (1.0 ~ #10515) or with Pharo 1.4 #14457.

b) We are told to add a class comment for the new class in the new package in the pane below the code-editing pane.

There is no pane below the code-editor.

What you need to do is click on the button labelled '?' below the Class listing pane, and that swaps editor pane to work on the Class comment.

Meta-click does not work on 3 button mouse on Windows 7

Shift-Alt-Left-click is the default workaround.

So you cannot bring up the Morphic halo for the LOCell instance
There is no Preferences Browser in Pharo 1.4 #14457

d) Method 2.9

is mouseAction and should be mouseAction: ?  i.e. should be a setter, not a getter.

e) Filing out is to the Contents | Resources folder of the Pharo install

How to kill the BouncingAtomsMorph window in Pharo By Example

Right-click the window
left-click the box at the left-hand end of the 'Select'/'deselect' option's row
   - this brings up the 'Morphic halo' or clickable buttons to do with sub-window management
left-click the window-close Morphic button - the X at the top-left of the window

The halo *should* appear if you meta-click (i.e. middle-click) the window - but it doesn't on my Windows install of the PBE-OneClick zip (which is id-ed as Pharo-1.0 Latest update: #10517 in System | About )

The BouncingAtomsMorph window - with and without the Morphic halo

Friday, 16 October 2015

Starting again, part 2

Basic loops
Loops are high-level collection iterators, implemented as regular methods.  


Transcript open.

1 to: 100 do:
  [:i | Transcript show: i asString; cr ]. 

1 to: 100 by: 3 do: [:i | Transcript show: i asString; cr]. 

The message do: is sent to a collection of objects (Array, Set, OrderedCollection), evaluating the block for each element.

#(11 38 3 -2 10) do: [:each |
     Transcript show: each printString; cr].  "do for each element in the array #(11 38 3 -2 10) and print to the Transcript"

"Some other really nice iterators"

#(11 38 3 -2 10) collect: [:each | each abs].  "collect means do for each and every member ?"

#(11 38 3 -2 10) collect: [:each | each odd].

#(11 38 3 -2 10) select: [:each | each odd].  "add each member that meets the criterion to a new collection?  Or to the subject of the message?"

#(11 38 3 -2 10) select: [:each | each > 10].

#(11 38 3 -2 10) reject: [:each | each > 10]. "remove each member that meets the criterion from the collection?  Or creates a new collection with the subset?"

#(11 38 3 -2 10)
     do: [:each | Transcript show: each printString]
     separatedBy: [Transcript show: '.'].

All objects understand this message.  It opens an information window about the object.  All it's properties.

brings up the Package Browser, which is the Class Browser plus info about packages (groups of classes)

Starting again

Time to retrace my steps, so I am sure I understand code that I read later

Download the PBE-OneClick, and started my way through it

ProfStef lessons 1st, then Pharo By Example, the book

Highlight text in the workspace and Alt-p or rt-click to "print it", and the expression is evaluated, then printed to the workspace.

It stays highlighted, so it is easy to then delete or backspace

Highlight text in the workspace and Alt-d to "do it".  Expression is evaluated.

Unary - one argument following an object

Binary one argument between two objects

Keyword - as many arguments as you like, each with a keyword preceding it.

Each keyword is suffixed by a colon.  e.g. Gordon middleName: 'Reynolds' lastName: 'Love'.

. (full stop) completes an expression

Precedence:  Unary then binary then keyword, left to right when of equal precedence

Always - even for arithmetic expressions.

Parentheses alter the precedence order

If you end an expression with ; (rather than . ), the subject of the next line is left implicit, and it will be the same as the subject of the line just ended.  This is called a message cascade.

Transcript show: 'hello'.
Transcript show: 'Smalltalk'.
Transcript cr.

"is equivalent to:"

  show: 'hello';
  show: 'Smalltalk' ;

Blocks are
   anonymous methods that can be
      stored as variables and
      executed on demand
   delimited by square brackets [ ]

Here is a block that adds 2 to its argument (its argument is named x):"  :x is, in effect, also the local variable.

[:x | x+2].

"We can execute a block by sending it value messages."

[:x | x+2] value: 5.

[Browser open] value.

[:x | x+2] value: 10.

[:x :y| x + y] value:3 value:5.

Declare a variable, enclose it in | |  e.g.

Blocks can be assigned to a variable, and executed later
b := [:x | x+2].
b value: 12.

Sends '12' into b, which is an object which takes its input and adds 2 to it.

Transcripts are little workpads on the screen
Transcript open.

Conditionals - messages sent to boolean objects

3 > 10 
ifTrue: [Transcript show: 'maybe there''s a bug ....']
ifFalse: [Transcript show: 'No : 3 is less than 10'].