Last active
December 18, 2022 23:10
-
-
Save tbrunz/02d9a2f62053f8dfa4c229e5075d6796 to your computer and use it in GitHub Desktop.
Tips on getting started with Pharo Smalltalk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Tips on getting started with Pharo Smalltalk: | |
The Pharo Playground & Transcript | |
The Pharo IDE (specifically, a Playground plus the Transcript) is very useful | |
for creating and executing "code snippets" or even entire scripts in Pharo. | |
It's very easy to get a Pharo IDE installed and running by just downloading and | |
installing Pharo Launcher from https://pharo.org/, cloning a template image, | |
then launching the image. | |
The Pharo 'REPL' (Read-Execute-Print-Loop), or 'CLI' (command line interface) | |
is a little different in Pharo than in other interpreted languages such as | |
Bash, Lua, Julia, etc. Since some Pharo constructs need more than one line | |
(without forcing the use of escapes such as '\'), and because you have more | |
than one way to view the results of evaluating something in Pharo, its REPL | |
is necessarily different. | |
In the Pharo IDE, you start by opening a 'Playground' window, using the menu bar | |
at the top or by left-clicking the desktop (also called 'The World') to get | |
a context menu. A Playground window is like a cross between a 'smart editor' | |
and a CLI that allows you to compose and execute groups of multi-line Pharo | |
statements and expressions. You can open several Playgrounds at once, and even | |
join multiple windows into a single tabbed window if you wish. | |
You'll quickly realize that pressing 'Enter' doesn't evaluate code statements. | |
Instead, you just get a new line to continue composing. When you're ready to | |
evaluate your statements, you use the mouse (or the keyboard) to swipe & select | |
the lines (or any amount of text) you want to evaluate, then right-click on the | |
highlighted text to choose an action to take. (You can also press control-key | |
equivalents to limit mouse use.) | |
When evaluating selected text, you have three choices for what results you want: | |
1) Execute what you've selected but ignore the result ('Do it'/Ctrl-D), | |
2) Execute and display the result under the cursor ('Print it'/Ctrl-P), or | |
3) Execute and open an inspector on the result ('Inspect it'/Ctrl-I). | |
The cool thing is that this ability to select-and-execute text actually works | |
*anywhere* you can enter or select text in the Pharo environment -- anywhere, | |
in any window! | |
The Playground window adds an extra feature: You can tell it to take the entire | |
contents of the Playground, execute it top-to-bottom, and open an inspector on | |
the result in a second tab. This is 'Do it & go'/Ctrl-G, which has not only a | |
context menu item, but also a green 'Play' icon in the Playground toolbar. | |
(The 'Do it & go' inspector is also more full-featured: You can cascade it, | |
inspecting elements of the first result, elements of that inspection, etc.) | |
It's called a 'Playground' because it lets you "play around with code", e.g., | |
to compose & test methods in an experimental fashion. Once you're satisfied | |
your ideas are working the way you want, you can swipe the code and copy/paste | |
it into a class method in a System Browser window. (Making your Playground | |
code into a method makes it persistent and call-able.) | |
One more thing you occasionally want a REPL to do is print things in a more | |
permanent form, i.e., log the results of evaluating some expressions. There's | |
a logging console window called the 'Transcript' for that, which is also opened | |
via the menu bar or from a left-click on the desktop. To print things on the | |
Transcript, you add the equivalent of 'print statements' to your Playground | |
script. | |
It might seem strange at first that the input and output for a REPL is split | |
between two windows. But it's actually a big advantage, especially when you | |
want to re-execute a group of lines you previously typed in the Playground: | |
You won't have your output intermixed with your command lines, so you can | |
easily re-select any of your input lines and run them again, which avoids | |
syntax errors and unwanted results caused by prompts and output lines being | |
included. (And many times you probably don't care to persist the output | |
anyway, especially if you're experimenting.) With the Transcript to capture | |
your results, you can retain everything you want to keep, and nothing that | |
you don't. This keeps your Playground clean, a nice feature! | |
Making Things Happen in Pharo | |
In Pharo, everything is done by sending messages to objects; there is no other | |
way to do anything! (It's a very simple programming paradigm...) So to get | |
the Transcript to 'log' the results of evaluating an expression, you send it | |
a message with an argument to be displayed. Here's a simple example: | |
circumference := 2 * Float pi. | |
Transcript crShow: circumference. | |
>>> 6.283185307179586 | |
which will display the circumference of a unit circle in the Transcript. How | |
does this work? 'Float' is the class of floating-point numbers, and, since | |
everything in Pharo is an object, a class is both a class and an object itself: | |
it can receive and act on messages, too. We send Float the (unary) message 'pi', | |
causing it to return pi as a float object, which we then multiply by 2 (by giving | |
pi to '2' and asking it to multiply itself by this value). We assign the result | |
to our variable, 'circumference'. Note that a period separates Pharo statements, | |
just as with English sentences. | |
In the second statement, we send the Transcript object (also a class object) | |
the 'crShow:' message, which includes an argument, the object 'circumference'. | |
It responds by printing a newline in the Transcript window, followed by the | |
(default) string representation of the float held by 'circumference', which is | |
"6.283185307179586". (Remember that you can evaluate *anywhere* in Pharo? | |
You can also enter these expressions directly in the Transcript window and | |
evaluate them in the Transcript window, too!) | |
Here's a more involved example: | |
x := 7. | |
y := 3. | |
z := [ :offset | x factorial + offset ]. | |
Transcript crShow: (z value: y squared). | |
>>> 5049 | |
In this case we define variables 'x' & 'y' and give them values 7 & 3. Then | |
we define variable 'z'... What are we assigning to 'z'? In Pharo, we can | |
defer execution of code statements by enclosing any number of them in square | |
brackets, making a 'block closure' object. These 'blocks', as they are called, | |
can be assigned to variables, passed as method arguments, & returned as results. | |
A block can also be instructed to do things, such as evaluate itself (which is | |
similar to making a function call). Evaluating a block can includes providing | |
it with one or more variable arguments. | |
In our example above, we've defined one formal argument for our block, 'offset'. | |
Formal parameters are denoted by adding ':' to the front of a variable name, | |
then separating the parameter list from the body with a '|' character. (We can | |
also add local variables to the block, if needed, by following the first '|' | |
with a '| varList |' expression.) We are then free to use these locals and | |
formal parameters within the block body statements. | |
We can also use any other variables that happen to be in scope when the block | |
is defined, including variables defined in the enclosing code -- even when we | |
know the enclosing code will have completed execution and had its locals go out | |
of scope! We say that variables 'x' & 'y' are "lexically scoped" in the block | |
we're defining, and can be referenced when the block executes (even if their | |
values change after the block is defined at run time). In Pharo, all blocks | |
are this kind of 'lexical closure' (a very powerful property that not many | |
programming languages provide). | |
The block code in our example first sends the unary message 'factorial' to the | |
value 'x' holds (which will be '7', a SmallInteger instance), causing it to | |
evaluate '7!'. This results in 5040, another instance of SmallInteger. That | |
object, '5040', is then given the binary message '+ offset', which tells it to | |
add the value of 'offset' to itself. But 'offset' will not take a value until | |
the block is given an argument when it's told to evaluate itself... | |
That evaluation takes place in the last statement, where we send Transcript the | |
keyword message 'crShow:' (a message with one argument). The argument in this | |
case is the result of yet another expression, 'z value: y squared', which we | |
want evaluated first. We force this by the use of parentheses; without the | |
parentheses, Transcript would receive the two-argument 'keyword' message, | |
'crShow:value:', which it would not understand, as that message is undefined. | |
Within the parentheses, 'y' (an instance of SmallInteger) first evaluates the | |
unary message 'squared'; in this case, the '3' returns '9' as its result. The | |
object '9' becomes the argument for the keyword message 'value:' that is sent | |
to 'z'. Since 'z' holds a one-argument block, its block receives the 'value:' | |
message, using the argument (9) to set its internal parameter, 'offset'. Then | |
the block evaluates itself, computing '7! + 9', or '5049'. | |
Finally, this result is passed to the Transcript as an argument to the 'crShow:' | |
message, instructing Transcript to print a newline in the Transcript window, | |
followed by a string representation of the SmallInteger '5049'. | |
Why did we need parentheses only in one case here? Pharo has three types of | |
messages: unary, binary, and keyword; each is illustrated above. The order of | |
precedence in evaluation is: unary, binary, then keyword; evaluation is always | |
performed left to right. Parenthesis are used to group expressions to override | |
this precedence. However, in practice, expressions will often have the desired | |
order of execution and won't need parentheses. (One possibly surprising | |
exception is an expression such as "3 + 2 * 4", which evaluates to 20, not to | |
11. Why? Because there are no exceptions to the evaluation rules, so it's | |
strict left-to-right: 3+2= 5, and 5*4= 20. Use parenthesis to make conventional | |
arithmetic rules apply!) | |
Where to go Next | |
Knowing the above is enough to start evaluating things in the Pharo IDE. The | |
IDE also has tools to help you find things, such as classes and methods, for | |
doing more sophisticated things. One of the hardest things about learning any | |
OOP language is knowing what the base classes are and what/how they do things | |
for you. Pharo has probably the best built-in set of tools to make finding | |
things easy. It's also a very explorable environment, providing all its source | |
code for you to browse, and even modify! Many methods and classes also contain | |
documentation that includes examples for how to use them. | |
If you're interested in learning how to create your own Packages, Classes, and | |
Methods in Pharo, it's recommended to continue beyond "ProfStef" and read at | |
least "Pharo by Example" (PBE), the first tutorial of an excellent 3-booklet | |
series that teaches the basics. The current version is being written for | |
Pharo 9, but the Pharo 5 version is still relevant, as most changes involve | |
internal improvements, with some GUI differences. Current publications are here: | |
https://books.pharo.org/ | |
Drafts of the soon-to-be-released version of PBE for Pharo 9 can be found | |
here: | |
https://github.com/SquareBracketAssociates/NewPharoByExample9/releases/tag/latest | |
There are other sources of help in getting started and getting answers to Pharo | |
questions, beyond just googling, including a Discord channel and several forums. | |
Refer to the main website, https://pharo.org/, for links. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Awesome stuff fresh off the presses!