Variables in Pharo
Marcus Denker, Inria
http://marcusdenker.de
Plan for an interactive
Exploration
• These Slides where done as an outline / plan for an
interactive exploration

• They might therefore be not exactly the same as the
content of the Demo
Variables in ST80
• Temporary Variable

• Instance Variable, Class Instance Variable

• Class Variable (and Pool Variable)

• Globals

• Pseudo Variables: self, super, thisContext
Instance Variables
• De
fi
ned by the Class (or Trait)

• Can be read via the object:

• instVarNamed:(put:), #instVarAt:(put:
)

• Instance Variables have an o
ff
set in the Object

• De
fi
ned by the order of the de
fi
ned vars in the Hierarchy
1@2 instVarNamed: 'x'
Temporary Variable
• De
fi
ned by a method or Block

• Arguments are temps, too

• Can be read via the context

• #tempNamed:, tempNamed:put
:

• With Closures this is more complex than you ever want to
know!
[| temp | temp := 1. thisContext tempNamed: 'temp' ] value
Globals
• Entries in the “Smalltalk globals” Dictionary

• Contain the value

• Can be read via the global Dictionary

• Access via #value / value: on the Association

• Class Vars and Pool Vars are just Associations from other
Dictionaries
Smalltalk globals at: #Object.
Object binding value.
“Everything is an Object”
• For Variables… not really
Globals/Class Vars
• Here we have at least the Association (#binding):

• But there is no “GlobalVariable” class

• No API other than #value:/#value

• Classes de
fi
ne just names of variables
Object binding
Instance Variables
• The class just knows the names

• There is no Object representing instance variables

• Classes de
fi
ne just names of variables

• Bytecode accesses by o
ff
set
Point allInstVarNames
Temporary Variables
• The methods know nothing. Even to know the variable
name we need the compiler (and the source)

• There is no object representing temp Variables

• Re
fl
ective read and write is *hard* -> compiler needs to
create extensive meta-data
Why Not Do Better?
• Of course memory was a concern in 1980, but today we
should be able to do better!

• Why not have objects (and a class Hierarchy) that
describes all Variables in the system?
Variables
• Every de
fi
ned Variable is described a meta object

• Class Hierarchy: Variable

• GlobalVariable

• ClassVariable

• Temporary Variables

• Instance Variables (aka Slots)
The Hierarchy
• Variable

• LiteralVariable

• ClassVariable

• GlobalVariable

• UndeclaredVariable

• WorkspaceVariable
• LocalVariable

• ArgumentVariable

• TemporaryVariable

• ReservedVariable

• SelfVariable

• SuperVariable

• ThisContextVariable

• Slot
Example: vars of a class
• Get all Variables of a class

• Inspect it

• #usingMethods
Point instanceVariables
Instance Variable
• Read x in a Point

• Write

• read/write without sending a message to the object
(Point instanceVariables
fi
rst) read: (5@4)
point := 5@4.
(Point instanceVariables
fi
rst) write: 100 to: point.
Globals
• Object binding class

• Object binding read

• We keep the Association API so the Global Variables can
play the role of associations in the global dictionary.
Object binding usingMethods
Temporary Variables
• There are too many to allocate them all

• They are created on demand (with the AST)
((LinkedList>>#do:) temporaryVariableNamed: 'aLink')
#lookupVar:
• Every variable knows the scope is was de
fi
ned in

• Every scope know the outer scope

• #lookupVar: looks up names along the scope
[ | temp |thisContext lookupVar: 'temp' ] value.
[ | temp |thisContext lookupVar: ‘Object' ] value
(Point slotNamed: #x ) scope outerScope
Debugger: Read Vars
• In the Debugger we to be able to read Variables from a
DoIt.

• lookupVar, then readInContext works for all Variables!

• DoItIn: uses this:
[ | temp | temp :=1 . (thisContext lookupVar: 'temp')
readInContext: thisContext] value
Context>>readVariableNamed: aName
^ (self lookupVar: aName) readInContext: self
Variables as AST
Annotations
• Pharo uses the RB AST

• RBVariableNode instance for every use of a Variable

• Annotated with subclasses of Variables:
(Point>>#x) ast variableNodes
fi
rst variable == (Point slotNamed: #x)
OCASTSemanticAnalyzer
• OCASTSemanticAnalyzer is the visitor that does the name
analysis

• Adds a scope for each block/the method

• Adds de
fi
ned variables to the scope

• Every RBVariableNode use will get annotated with the
variable that #lookUpVar:
fi
nds
Variables and Bytecode
• Compiler just delegates to the Variable

• InstanceVariableSlot>>#emitStore:

• emitStore/emitValue:
emitStore: methodBuilder
"generate store bytecode"
methodBuilder storeInstVar: index
Does that mean…
• If variables are de
fi
ned by a class, could we not make a
subclass?

• And even override the code generation methods ?!
Now let’s create our own
kind of Variable!
Lazy Variables
• Two ways to initialize instance state in ST80

• implement #initialize method (#new calls it)

• use accessors and lazy init pattern

• Can we not do better? Can the Variable not initialize
itself?
Lazy Variables
InstanceVariableSlot subclass: #LazyInitializedInstanceVariabl
e

instanceVariableNames: 'default
'

classVariableNames: '
'

package: ‘CompilerTalk
'

printOn: aStrea
m

aStream
 

store: self name
;

nextPutAll: ' => '
;

nextPutAll: self class name
;

nextPutAll: ' default: '
.

default printOn: aStrea
m
Lazy Variables
read: anObjec
t

"if the value is nil, we write the default value
"

^ (super read: anObject) ifNil: [
 

self write: default to: anObject
]

emitValue: aMethodBuilde
r

"generate bytecode for '<varname> ifNil: [<varname> := default]'
"

aMethodBuilde
r

pushInstVar: index
;

pushDup
;

pushLiteral: nil
;

send: #==
;

jumpAheadTo: #target if: false
;

popTop
;

pushLiteral: default
;

storeInstVar: index
;

jumpAheadTarget: #targe
t
Let’s use it
Object subclass: #MyClas
s

instanceVariableNames: 'var
'

classVariableNames: '
'

package: ‘CompilerTalk'
?
How can we make ‘var’ to be a LazyInitializedInstanceVariable?
Class De
fi
nition
• We need a new way to de
fi
ne classes: Fluid Class
De
fi
nition

• uses cascade for extensibility

• no string, but {} arrays for variables
Fluid Class De
fi
nition
Object << #Poin
t

slots: { #x . #y }
;

tag: 'BasicObjects'
;

package: 'Kernel'
Fluid Class De
fi
nition
• Pharo9: Default is the ST80 style class de
fi
nition

• Fluid can be enabled

• It is used automatically when needed (when using a self
de
fi
ned Variable, for example)

• Goal: Default for Pharo 10
Let’s use it
Object << #MyClas
s

slots: { #var }
;

package: ‘CompilerTalk'
#notation for normal instance Variables
Let’s use it
Object << #MyClas
s

slots: { #var => LazyInitializedInstanceVariable default: 5 }
;

package: ‘CompilerTalk'
For de
fi
ning other variables: use =>
MyClass new var
.

(MyClass new var: 8) var
Inspect method to see bytecode
put halt in read: method of Slot
thisProcess
• To get the current Process we use a message send to a
global variable. But we could use a variable like
thisProces
s

• This avoids a message send (and possible interrupt
check) as we can emit a bytecode
self, thisContext…
• (Object lookupVar: ‘thisContext’) usingMethods
• See classes

• ThisContextVariable

• SelfVariable

• SuperVariable
The Code
ReservedVariable << #ThisProcessVariable
slots: {};
tag: 'Variables';
package: 'Kernel'
emitValue: methodBuilder
methodBuilder pushThisProcess
======
class side:
variableName
^ 'thisProcess'
======
Smalltalk globals resetReservedVariables
Compatibility
• Fully backward compatible: we can load ST80 style class
de
fi
nitions (and Pharo9 just shows this view by default)

• Re
fl
ective API is compatible: “instVarAt:”… still exist

• If you want to restrict yourself to ST80, a checker could
be easily created
Compatibility
• But if you start to use your own kind of Variables, you
code will not be “ST80” compatible anymore

• But you will be able to use the power of the new
abstraction provided

• Was this not the original idea behind Smalltalk? That a
Programming System is a Medium?
Where do we use it?
• We are careful! We use Pharo ourselves… 

• We need a stable system to work with

• We need to learn about how to use Variables best

• Variables are used by the Compiler internally

• Every instance variable is an InstanceVariableSlot (Globals, class vars)

• Variables are used by the Debugging infrastructure to read/write

• replaced the DebuggerMethodMap

• The Spec UI Framework uses ObservableSlot
Next Steps Variables
• DoitVariable for nicer code in #DoItIn: methods 

• Undeclared Variables

• programmer interaction in read/write, not compile!

• better behaviour for test-
fi
rst development

• Implement ThisProcessVariable in Pharo10

• Use WeakSlot to simplify some code
Next Steps Fluid Classes
• Finish the last problems (see issue tracker), make it the
default

• Experiments with Meta Data for Classes

• Tag abstract classes

• Experiment with Pragmas for classes

• Compiler, compiler plugging, compiler options
More..
• Extend the MetaLink Model to allow MetaLinks on
Variables (
fi
rst code is there already)

• More Experiments about Slot Composition 

• Implement Default value once, use it on ClassVariable,
InstanceVariableSlot and WeakSlot

• First prototype, but it turned out to be too complex
Thanks…
• This is the work on *many* contributors from the Pharo
Community

• Thanks for lots of interesting discussions, ideas, and
code!
Help Wanted
• We are always interested in improvements!

• Pharo 10 is under active development

• 30-40 Pull Requests integrated per week

• Your Improvements are Welcome!
https://github.com/pharo-project/pharo
Questions?

Variables in Pharo

  • 1.
    Variables in Pharo MarcusDenker, Inria http://marcusdenker.de
  • 2.
    Plan for aninteractive Exploration • These Slides where done as an outline / plan for an interactive exploration • They might therefore be not exactly the same as the content of the Demo
  • 3.
    Variables in ST80 •Temporary Variable • Instance Variable, Class Instance Variable • Class Variable (and Pool Variable) • Globals • Pseudo Variables: self, super, thisContext
  • 4.
    Instance Variables • De fi nedby the Class (or Trait) • Can be read via the object: • instVarNamed:(put:), #instVarAt:(put: ) • Instance Variables have an o ff set in the Object • De fi ned by the order of the de fi ned vars in the Hierarchy 1@2 instVarNamed: 'x'
  • 5.
    Temporary Variable • De fi nedby a method or Block • Arguments are temps, too • Can be read via the context • #tempNamed:, tempNamed:put : • With Closures this is more complex than you ever want to know! [| temp | temp := 1. thisContext tempNamed: 'temp' ] value
  • 6.
    Globals • Entries inthe “Smalltalk globals” Dictionary • Contain the value • Can be read via the global Dictionary • Access via #value / value: on the Association • Class Vars and Pool Vars are just Associations from other Dictionaries Smalltalk globals at: #Object. Object binding value.
  • 7.
    “Everything is anObject” • For Variables… not really
  • 8.
    Globals/Class Vars • Herewe have at least the Association (#binding): • But there is no “GlobalVariable” class • No API other than #value:/#value • Classes de fi ne just names of variables Object binding
  • 9.
    Instance Variables • Theclass just knows the names • There is no Object representing instance variables • Classes de fi ne just names of variables • Bytecode accesses by o ff set Point allInstVarNames
  • 10.
    Temporary Variables • Themethods know nothing. Even to know the variable name we need the compiler (and the source) • There is no object representing temp Variables • Re fl ective read and write is *hard* -> compiler needs to create extensive meta-data
  • 11.
    Why Not DoBetter? • Of course memory was a concern in 1980, but today we should be able to do better! • Why not have objects (and a class Hierarchy) that describes all Variables in the system?
  • 12.
    Variables • Every de fi nedVariable is described a meta object • Class Hierarchy: Variable • GlobalVariable • ClassVariable • Temporary Variables • Instance Variables (aka Slots)
  • 13.
    The Hierarchy • Variable •LiteralVariable • ClassVariable • GlobalVariable • UndeclaredVariable • WorkspaceVariable • LocalVariable • ArgumentVariable • TemporaryVariable • ReservedVariable • SelfVariable • SuperVariable • ThisContextVariable • Slot
  • 14.
    Example: vars ofa class • Get all Variables of a class • Inspect it • #usingMethods Point instanceVariables
  • 15.
    Instance Variable • Readx in a Point • Write • read/write without sending a message to the object (Point instanceVariables fi rst) read: (5@4) point := 5@4. (Point instanceVariables fi rst) write: 100 to: point.
  • 16.
    Globals • Object bindingclass • Object binding read • We keep the Association API so the Global Variables can play the role of associations in the global dictionary. Object binding usingMethods
  • 17.
    Temporary Variables • Thereare too many to allocate them all • They are created on demand (with the AST) ((LinkedList>>#do:) temporaryVariableNamed: 'aLink')
  • 18.
    #lookupVar: • Every variableknows the scope is was de fi ned in • Every scope know the outer scope • #lookupVar: looks up names along the scope [ | temp |thisContext lookupVar: 'temp' ] value. [ | temp |thisContext lookupVar: ‘Object' ] value (Point slotNamed: #x ) scope outerScope
  • 19.
    Debugger: Read Vars •In the Debugger we to be able to read Variables from a DoIt. • lookupVar, then readInContext works for all Variables! • DoItIn: uses this: [ | temp | temp :=1 . (thisContext lookupVar: 'temp') readInContext: thisContext] value Context>>readVariableNamed: aName ^ (self lookupVar: aName) readInContext: self
  • 20.
    Variables as AST Annotations •Pharo uses the RB AST • RBVariableNode instance for every use of a Variable • Annotated with subclasses of Variables: (Point>>#x) ast variableNodes fi rst variable == (Point slotNamed: #x)
  • 21.
    OCASTSemanticAnalyzer • OCASTSemanticAnalyzer isthe visitor that does the name analysis • Adds a scope for each block/the method • Adds de fi ned variables to the scope • Every RBVariableNode use will get annotated with the variable that #lookUpVar: fi nds
  • 22.
    Variables and Bytecode •Compiler just delegates to the Variable • InstanceVariableSlot>>#emitStore: • emitStore/emitValue: emitStore: methodBuilder "generate store bytecode" methodBuilder storeInstVar: index
  • 23.
    Does that mean… •If variables are de fi ned by a class, could we not make a subclass? • And even override the code generation methods ?!
  • 24.
    Now let’s createour own kind of Variable!
  • 25.
    Lazy Variables • Twoways to initialize instance state in ST80 • implement #initialize method (#new calls it) • use accessors and lazy init pattern • Can we not do better? Can the Variable not initialize itself?
  • 26.
    Lazy Variables InstanceVariableSlot subclass:#LazyInitializedInstanceVariabl e instanceVariableNames: 'default ' classVariableNames: ' ' package: ‘CompilerTalk ' printOn: aStrea m aStream store: self name ; nextPutAll: ' => ' ; nextPutAll: self class name ; nextPutAll: ' default: ' . default printOn: aStrea m
  • 27.
    Lazy Variables read: anObjec t "ifthe value is nil, we write the default value " ^ (super read: anObject) ifNil: [ self write: default to: anObject ] emitValue: aMethodBuilde r "generate bytecode for '<varname> ifNil: [<varname> := default]' " aMethodBuilde r pushInstVar: index ; pushDup ; pushLiteral: nil ; send: #== ; jumpAheadTo: #target if: false ; popTop ; pushLiteral: default ; storeInstVar: index ; jumpAheadTarget: #targe t
  • 28.
    Let’s use it Objectsubclass: #MyClas s instanceVariableNames: 'var ' classVariableNames: ' ' package: ‘CompilerTalk' ? How can we make ‘var’ to be a LazyInitializedInstanceVariable?
  • 29.
    Class De fi nition • Weneed a new way to de fi ne classes: Fluid Class De fi nition • uses cascade for extensibility • no string, but {} arrays for variables
  • 30.
    Fluid Class De fi nition Object<< #Poin t slots: { #x . #y } ; tag: 'BasicObjects' ; package: 'Kernel'
  • 31.
    Fluid Class De fi nition •Pharo9: Default is the ST80 style class de fi nition • Fluid can be enabled • It is used automatically when needed (when using a self de fi ned Variable, for example) • Goal: Default for Pharo 10
  • 32.
    Let’s use it Object<< #MyClas s slots: { #var } ; package: ‘CompilerTalk' #notation for normal instance Variables
  • 33.
    Let’s use it Object<< #MyClas s slots: { #var => LazyInitializedInstanceVariable default: 5 } ; package: ‘CompilerTalk' For de fi ning other variables: use => MyClass new var . (MyClass new var: 8) var Inspect method to see bytecode put halt in read: method of Slot
  • 34.
    thisProcess • To getthe current Process we use a message send to a global variable. But we could use a variable like thisProces s • This avoids a message send (and possible interrupt check) as we can emit a bytecode
  • 35.
    self, thisContext… • (ObjectlookupVar: ‘thisContext’) usingMethods • See classes • ThisContextVariable • SelfVariable • SuperVariable
  • 36.
    The Code ReservedVariable <<#ThisProcessVariable slots: {}; tag: 'Variables'; package: 'Kernel' emitValue: methodBuilder methodBuilder pushThisProcess ====== class side: variableName ^ 'thisProcess' ====== Smalltalk globals resetReservedVariables
  • 37.
    Compatibility • Fully backwardcompatible: we can load ST80 style class de fi nitions (and Pharo9 just shows this view by default) • Re fl ective API is compatible: “instVarAt:”… still exist • If you want to restrict yourself to ST80, a checker could be easily created
  • 38.
    Compatibility • But ifyou start to use your own kind of Variables, you code will not be “ST80” compatible anymore • But you will be able to use the power of the new abstraction provided • Was this not the original idea behind Smalltalk? That a Programming System is a Medium?
  • 39.
    Where do weuse it? • We are careful! We use Pharo ourselves… • We need a stable system to work with • We need to learn about how to use Variables best • Variables are used by the Compiler internally • Every instance variable is an InstanceVariableSlot (Globals, class vars) • Variables are used by the Debugging infrastructure to read/write • replaced the DebuggerMethodMap • The Spec UI Framework uses ObservableSlot
  • 40.
    Next Steps Variables •DoitVariable for nicer code in #DoItIn: methods • Undeclared Variables • programmer interaction in read/write, not compile! • better behaviour for test- fi rst development • Implement ThisProcessVariable in Pharo10 • Use WeakSlot to simplify some code
  • 41.
    Next Steps FluidClasses • Finish the last problems (see issue tracker), make it the default • Experiments with Meta Data for Classes • Tag abstract classes • Experiment with Pragmas for classes • Compiler, compiler plugging, compiler options
  • 42.
    More.. • Extend theMetaLink Model to allow MetaLinks on Variables ( fi rst code is there already) • More Experiments about Slot Composition • Implement Default value once, use it on ClassVariable, InstanceVariableSlot and WeakSlot • First prototype, but it turned out to be too complex
  • 43.
    Thanks… • This isthe work on *many* contributors from the Pharo Community • Thanks for lots of interesting discussions, ideas, and code!
  • 44.
    Help Wanted • Weare always interested in improvements! • Pharo 10 is under active development • 30-40 Pull Requests integrated per week • Your Improvements are Welcome! https://github.com/pharo-project/pharo
  • 45.