I've found a good way to approach Smalltalk systems like Pharo and Squeak is to remember they're whole systems, not just languages with a bolted-on IDE.
I like to think of each running instance as more akin to a Unix virtual machine than anything else. You end up using "in-image" version control systems like Monticello just the same way on a Unix server you use "in-operating-system" version control systems like git.
i love the serendipity of this topic coming up just days after that post you found when i have not looked into it for a few years after trying for a few days to get that original squeak terminal to run in pharo.
Good to see this make the front page of HN. I only spend a few hours a month using Pharo (some working on my open source NLP project, but mostly just trying out new packages and demos released for Pharo).
Other comments here are right: think of Smalltalk as being the complete computer. When I use Pharo I usually put it in full screen mode and don't think about the macOS or Linux host.
I've been visiting HN for quite a few years now, but somehow this is my first encounter of a topic on Pharo. The language seems interesting to me, so I am glad it was posted today.
I'd say def not rare, but it never feels very common to me either relative to the posts on similar technologies like Lisp, Prolog...etc. So I can understand the parent comments view.
For anybody who's used an "immersive" programming environment, like Smalltalk to a Lisp machine -- what's the advantage? It seems like the ability to mutate the whole world would just be asking for mistakes. I can barely keep a clean Python REPL. Do you restart your whole machine to run your test suite?
2. How do you manage the state of the environment?
The advantage is programming-as-teaching, as opposed to programming-as-carpentry. What I mean by "programming-as-teaching" is that old-fashioned Smalltalk (and Lisp) enviroments like Pharo are designed to support a style of development where you build a program by starting up the runtime and teaching it interactively, incrementally, how to be the program you want.
(What I mean by "programming-as-carpentry" is the more widespread and better-known model in which you are essentially working to build a planned artifact from a blueprint, and your development environment is analogous to a workbench or toolchest, rather than a student.)
If you don't like programming-as-teaching, then it's not an advantage. Most people don't work that way, but I think that's mostly because they don't even know it's an option. I'm not saying that everyone would prefer programming-as-teaching, but it gets so little exposure that I think it's a safe bet that more people would like it if they knew what it was.
I prefer programming-as-teaching over the alternative. I am dramatically more productive when I can work that way. I've been one of those fabled 10X programmers, as confirmed by VCS statistics, when working with such tools. It wasn't because I'm so brilliant or because I have amazing magical powers; it's because we were working with that kind of environment, and I knew how to use it and the other programmers didn't.
So, in brief, the advantage is that if you have such an environment and can use it effectively, you can be dramatically more productive. It changes the feel of programming from "let's build this variant and see how it turns out" to "reach over and correct that student's posture." The cycle of iterations becomes very fast, so fast that sometimes the duration of a cycle is imperceptible.
How do you manage the environment? First, let me acknowledge that modifying your working environment willy nilly is indeed a problem. The solution is that you don't do that.
Instead, you start from a known good system image. Environments like Pharo and other old-fashioned Smalltalk and Lisp environments offer a feature that can save the entire state of system memory to an "image" file. If you start the environment from an image, you get an in-memory state that is an exact duplicate of the state the environment was in when saved--right down to the positions and contents and activation states of all the windows. You start the image, and you're in the environment, exactly as it was when you saved it.
The way you manage incremental state changes is that you make a reference image that contains a known good state. A common choice for a reference image is a clean, unmodified release image. Then you make any site-specific modifications and customizations and save the result as a reference image as well. Then you save a working image based on that reference image and make your changes to the working image.
This practice enables you to easily recover if you get into a bad state. It's very fast to kill a bad session and restore a reference image. For example, a Pharo 6.1 image launches in under half a second on my older iMac. Clozure Common Lisp starts from an image in a similar amount of time.
When I've done ongoing serious work using this kind of system, we kept orderly collections of reference and working images--reference images that served as checkpoints in case of trouble; working images in which we had all the stuff we were working on loaded up and ready to go.
Besides making it quick and easy to resume yesterday's work, and to recover in cases where we messed up, images also served the useful purpose of enabling us to capture precise dynamic states of work in progress. For example, if a rare bug arises, I can save a working image with the problem on the screen, and give that image to my colleagues. They can start up the image and immediately see the problem state on their screens, and use the introspective capabilities of the development system to diagnose the problem.
But I guess that's more an advantage than a how-do-you-manage-it observation.
This is a really great explanation, and I appreciate you taking time to share in such depth.
One question I have is on version control. I understand the value of reference images and release images and debugging images (with a rare bug front and center) and so on. Would this play well with diffing as I am familiar with it from a carpentry perspective, e.g git diff?
Would you serialize and dump a set of source code from an image? Would you build the reference image from a source code project and manually handle the consistency between source files and images?
I know it's a vague multi-question, but I hope you understand what I'm getting at, and I would really appreciate learning more about this style of development.
Most Squeak/Pharo Smalltalk development takes place these days in the context of either git itself or a git-like DVCS called Monticello.
Use of git/monticello within the image directly mirrors use of git at the command-line in Unix.
You use git/monticello to clone a remote repo, installing the code it contains into the live image. Because of other aspects of the Smalltalk design, this has an additional effect of being a bit like package installation - the code isn't just present, it's installed and ready to run. Unlike Unix, the lack of an explicit system-compilation step in Smalltalk means it's also ready to edit in-place, which means, if you've started some service instances, ready to edit live.
It's very much like Docker or working with a Unix VM: you start from a known image, like a Docker FROM command or a Debian ISO, and install files/code/packages from there, using version control tools or not as you like.
Other comments explain that nowadays Smalltalk programmers use git or Monticello for revision control. Lisp programmers usually use git or other conventional revision control tools.
Yes, traditional Smalltalk systems can serialize sources to files, and can read them back into a running image. In Smalltalk jargon that's called "filing out" and "filing in".
Lisp systems generally use plain old text files as the canonical form of storage for source code. A given project consists of a set of source files, just like a C or Java program. The utility of image files is that they give you (1) the starting Lisp environment; (2) a reference image customized for your local site that starts up fully customized and ready to go; and (3) working images that record a specific state that you were working on, to preserve your context.
Git and tools like it are not great for managing image files. Images are large binary files, and successive versions of images tend to have collections of small changes to them. This is exactly the kind of file that git does not handle well at all. Consequently, I don't generally use git or similar tools to manage images, and I don't imagine a lot of other Smalltalk and Lisp programmers do, either (correct me if I'm wrong!).
It would be great if we had some sort of git for large binary files. I don't mean something like git-annex--a way for git to duck the problems of managing versioned large binary files by outsourcing them to some other storage. I mean it would be great to have a revision-control system that could manage versioned binaries as well as git manages versioned text.
Lisp and Smalltalk programmers are not the only people who would benefit. Any project that needs to manage a lot of media files would benefit. One obvious example is in game development. Even small games tend to need to manage versioned files containing thousands of image and sound resources. It's a giant pain. Git for binaries would be a godsend for projects like that.
And for managing Lisp and Smalltalk image files, too, but we can't rely on that demand. The number of people who need to manage a lot of Lisp and Smalltalk images is even smaller than the number of people regularly using Lisp and Smalltalk in their daily work.
I feel like I should answer part of your question more directly than I did:
"Would you serialize and dump a set of source code from an image? Would you build the reference image from a source code project and manually handle the consistency between source files and images?"
Yes, this is a normal way to manage changes in Lisp projects.
I should mention that, even when I'm using images as I've described, I use text files, not image files, as the canonical storage for my code. I use images in the ways I've described, but I don't use them as the main way to store the reference version of my code. Consequently, the normal way I manage Lisp projects (and the way I've seen other Lisp hackers do it) is to use git or similar tools to manage versions of the source code. I build reference images, working images, and release images from those source files.
Smalltalk systems have always been more oriented toward managing everything from within the image, and the Smalltalkers I've worked with generally treated text files as a serialization format for exchanging code with others. The canonical form of the source code was in the image. (Well, really, it was in the sources file, but that's basically just backing store for the image.)
I work with Lisp much more than with Smalltalk, so I'll defer to other commenters on the current best practices for managing Smalltalk projects with revision-control tools.
I will mention that some older Lisp systems were a little more like Smalltalk systems, in that they had more tools for version management than currently-available systems. Interlisp, for example, had a pretty impressive change-management facility called Masterscope, and Lisp Machines had a versioned file system--the machine's filesystem was a revision-control system.
Exactly the same question ocurred to me as I was reading. In a lisp the is certainly possible, the environment exists as a giant sexp (possibly with some small compiled components). No idea how smalltalk works though.
I've tried something like this with ipython, since you can save the session's input as a .py file. As long as you don't depend on the outside world you can recreate your image by using the bare repl as your "base image" and just load the (gittable) previous sessions input (which itself possibly loads older and older sessions).
A Smalltalk system has data structures that record a comprehensive inventory of every memory object. If anything, Smalltalk makes it even easier than Lisp to keep track of all the objects in memory.
Something that just occurred to me after reading this description of it: would "programming-as-teaching" not be the ideal workflow for something like Kubernetes or Docker containers running in volatile cloud VMs?
Basically I'm a "code-as-carpentry" guy who's progressively being required to write applications in a way that DevOps can readily consolidate in our cloud provider for optimal resource use. But I don't have a lot of experience developing for this environment, so some problems like scheduling tasks are proving difficult (apparently this is doable with message queues but I haven't had much luck with that so far).
Your description sounds like it solves all of these problems. If you ran this in a container/pod/whatever, when it received a signal from DevOps to shut down for consolidation, it could immediately respond by saving the system state to an image and picking up from that state when the container/pod/whatever was started back up. This would also solve the problem of long-running processes since they could freeze in mid-run and pick back up like nothing happened, if I understand how this works.
Sounds promising. The trouble is that programming-as-teaching relies on a fairly rich set of runtime language features. The language runtime has to know a bunch of stuff about what's in memory in order to provide the programming-as-teaching features. That makes it hard to add the needed features as bolt-on extensions. You sort of need the whole environment to be designed for that kind of work from the ground up.
So if you're right that programming-as-teaching, and image-based development, is a good solution to the issues you face, then an old-fashioned Smalltalk or Lisp system seems like a good choice. If you want those features, but you don't want Lisp or Smalltalk, then I think your only option is to build a new development environment for your language of choice that has the requisite support.
That's a tall order.
Sometimes I've idly toyed with the idea of writing a Ruby implementation on top of a Common Lisp or Smalltalk runtime, but that would be a huge amount of work with no guarantee that there's any demand for it.
Isn't MagLev sort of a Ruby implementation on a Smalltalk runtime? I remember reading about it, but never used it. It's up on GitHub, but looks fairly dead:
Where does testing fit into programming by teaching? I've worked with people who write some code, refresh the screen, write some more code, refresh the screen, etc. Is that programming as teaching? They definitely produce at least 10x more code than I do, but it is instant legacy code with no tests. Do you go back and write tests after everything looks like it is working?
Other comments have pointed out that test-driven design was actually born in the Smalltalk community, which sort of answers your question, by implication, at least: it's standard operating procedure to write a lot of tests as you go.
Indeed, in my experience, any expression I write in the repl is more likely than not to be a test, or part of a test.
The way I normally work is with a running Lisp image and one or more files open in editors. For greenfield development I usually create a project and write a loadfile to load any library dependencies I expect to use. Once that's loaded, I write some expressions to build some test data, and then some more expressions to operate on the test data. I evaluate the test-data constructors and then fiddle with operations on the data to assure myself that what happens is what I expect to happen. Basically, I start with an idea of how to accomplish something, sketch out the data structures and APIs I think I'll need, and start exercising them to see what I got wrong, what I need to add or change.
The contents of those source files gradually become the source code of my program and of the test suite that go with it. When the project is new and small, the sources are a few files with a small number of lines of code, and the organization is minimal and loose. As I flesh out the program, the source files grow more numerous and more formally organized.
As a project gets complex, I may choose to save working images at points where I'm confident that the state of the system is good. Such images enable me to start the Lisp environment with all of my code already loaded and ready to use. I can then work from that point in the same way that I've described.
The normal way I work on anything, unless the tools I have to use prevent it, is to have a work-in-progress running, and use variable references and function calls in the repl to query the and modify the dynamic state. For any expression more complicated than a short one-liner, I actually write it in a source file and copy it to the repl (actually, I usually use Emacs with SLIME, so "copying it to the repl" really means typing a keystroke or two in the source file).
One of the differences between old-fashioned Lisp and Smalltalk systems and newer systems with repls is that the old Lisp and Smalltalk repls are capable of doing every part of program development--defining and redefining functions and data structures, running and debugging code, inspecting dynamic state, catching, inspecting, and modifying erroneous code, and building the finished app for deployment--all by typing expressions at the repl.
To put it another way, the entire language and development tools and all of your application's internals are always in memory and completely accessible from the repl.
It's not that you would normally do all your development in the repl, although you could if you wanted. Rather, the important point is that, because the repl can do anything the language and development tools can do, there are no artificial limitations on what you can do from the repl. Outside old Lisp and Smalltalk systems, every repl I've used always imposes some restrictions--some things you can't do from the repl. Invariably the way I discover this is by needing to do something in the repl and finding that it won't let me.
Thanks for the detailed explanation. I haven't used smalltalk, and I think making the mental shift from "source files and an interpreter or compiler" to "working in the image" is something I'll need to experience to fully appreciate.
It's easier to grok if you have a friend or colleague who is familiar with it and who can show you the ropes.
Also, there's no guarantee that you'll find it congenial. Some people profess to prefer programming-by-carpentry, and that's fine, of course.
In fact, maybe it's better for your sake if you don't like it. The great majority of programming work is programming-by-carpentry. You might find that you really like programming-by-teaching, and then spend the rest of your life pining for the opportunity to work that way.
Smalltalk is the home of a very early unit test framework, SUnit, which generated quite a few other frameworks. Kent Beck described it in his book Kent Beck's Guide to Better Smalltalk along with an earlier paper. http://swing.fit.cvut.cz/projects/stx/doc/online/english/too...
Test suites and test cases.
Tests are usually written first and drive the development.
When encountering a non existing message, the debugger pops up and asks you if you want to create the message.
Usually, this makes one think: "ha, this is not right" and then goes back on the execution stack and either fixes the test and continues or refactors something etc.
But in a dynamic language, tests are required to avoid regressions.
Programming as teaching goes both ways, programmer is teaching Smalltalk, Smalltalk helps in revealing thinking flaws to programmer. Loop continues.
Thank you for your example of programming-as-teaching vs programming-as-carpentry. Do you know of or maybe you've written something more in depth showing in detail examples how to build something using programming-as-teaching vs programming-as-carpentry?
I don't know of a succinct description of the process off the top of my head. Maybe I or another Lisper or Smalltalk should write one.
There are a few videos floating around the net that demonstrate some aspects of this style of work. Some are ancient promotional videos, like Symbolics people showing how to interact with a Lisp Machine. There are also a few more recent ones--for example, there are a couple of videos that Rainer Joswig made showing various interactions with Lisp systems.
Those videos don't really do the subject justice, though. They're short, and they show off one or a couple of nifty features, but programming-as-teaching is a whole-system approach, and it's hard to grasp it without comprehensive exposure to the whole of the system, and how the parts fit together.
I think that contributes to the obscurity of the subject: not only do few programmers get any exposure to these systems, but small exposure isn't really enough to explain what's distinctive about them. You need a pretty thorough exposure to the whole system and how its parts work together to understand what the deal is.
It's like using Unix more than using an IDE. The globals dictionary and class variables etc are roughly analogous to the filesystem. Ordinary avoidance of unnecessary global state / global variables does the trick. Just as in Unix you wouldn't go around messing with e.g. stuff in /lib, you don't tend to find yourself messing with "mutating the whole world" unless you're experimenting with the limits of the system for fun or doing something weird, metaprogramish, or systems-level.
Nowadays, an image should not be considered as a long lived artifact, one would have a CI job to create the image. See https://github.com/hpi-swa/smalltalkCI
There is also Metacello with BaselineOf constructs etc.
Advantage is that when you get an exception, you just go back in the stack, fix, and then proceed forward instead of looking at a stacktrace and wondering what happened (yes, pdb is nice but not the same feel at all).
Squeak is the most delightful IDE I've worked in. I liked the idea that the IDE, including the interactive debugger, was available to the user to program. I hope Pharo gains more traction and becomes popular at least in teaching environments.
I have a dream that some day we have statically typed language which would have first-class types with some compile time meta programming.
For given example data, type providers output just something and typically it's 95% what you want. But how to fix that 5%? You cannot just say that "Type.Property, could you please be an integer". Instead of you have to tweak example which in some case is just fine but on some cases it's not possible at all.
Example: you use these SQL libraries which are based on type providers. You have some clear schema and it's just works. Then some day you would like to use runtime defined columns but at that point you have to opt out from that type provider library (at least for queries related to that table).
I still love working with F# but I don't use much of type providers. I think they are like proof of concept that compile time meta-programming can be provide real value but it need still more freedom.
Edit:
I use SQL library called Rezoom and while it has it's limits it has given great benefit during development. When you change DB schema every effected query and model transformation gives compile time error. Value there is real.
Squeak is the original descendent from Smalltalk-80, where they used the last good image as a starting point for a bootstraped implementation of Smalltalk-80.
Pharo started as a kind of Squeak fork, where they wanted to pursue other ideas related to JIT, GC, compiler technology, traits and so on.
So basically both are Smalltalk-80 implementations, with Pharo trying to be a more modern version of it.
Thanks for the info! Very interesting about bootstrapping from the last good image. An old friend of mine (now retired) was involved in Squeak from before the beginning, and this reminds me that I've been meaning to catch up with him. I bet he has some interesting stories.
I tried out Squeak about ten years ago, but never could get past the awful bitmap font. Naturally, the first thing I noticed on the Pharo page was that the fonts look nice! So I installed it on my WQHD ThinkPad to try it out.
The first thing I noticed is the way it responds to the TrackPoint. It is not like any other application I have ever used. Instead of letting Windows control the pointer position, it's doing something very different, I just don't know what.
It's not so bad with the touchpad, but I don't use the touchpad except for scrolling.
It doesn't support high DPI either, so the text is all blurry. Way better than the old Squeak bitmap fonts, though.
I don't want to sound like a complainer, it just wouldn't be pleasant to program in this environment when every other app and dev tool I use supports high DPI and correct TrackPoint response, both on Windows and Linux.
The next-gen graphics system for Pharo, called Bloc, is still under development but making good progress. If you can find some of the demos [1] you'll see that things like text rendering are vastly improved.
As I understand it, pharo was squeak. But squeak with etoys was very much aimed at children - pharo was a fork to make a more "serious" open/free smalltalk. Since then the projects have moved forward together - and apart. Sharing advances (like new VM) where it makes sense.
But I'm sure there are people that know more on here. Like Alan Kay, for example :)
I left the Smalltalk world years ago (for Java ... hmm was it a win?) but the thing I miss the most is ... reading great code. Within the Smalltalk image/IDE, everyday you read code that other folks - many with brains much much bigger than mine - have written, and its great.
A lot of the code I read now (Java,Objective-C) is ... err ... brain hurt :-(
> everyday you read code that other folks - many with brains much much bigger than mine - have written, and its great.
I don't think that it's a language thing. It's probably popularity thing most of all.
In popular languages the quality of the average line of code is pretty low. Plus, you know, real-world constraints of real developers working under real-world constraints....
The funny thing that back in the NeXTSTEP days there were so few Objective-C books that I noticed myself and other programmers were buying Smalltalk books also (not that there were a lot of Smalltalk books).
All is nice and well... until you need to code with a screen-reader. That kind of sucks, as those IDEs obviously use some non-native controls and the only thing the screen-reader can see is a blank window with no usable content. I was once very excited about such projects, but I'm not anymore because I can't really use them in any way.
With normal languages like Java etc. You can just use a different, accessible IDE (i.e. Eclipse instead of Net Beans). That creates some problems, especially when working with a team, but in the end, it can be done if you try hard enough. Smalltalk and smalltalky things that try to be GUI only, no command line at all, are completely hopeless, though.
The more inaccessible UIs like this I encounter, the more I'm convinced that the future of accessibility is going to have to be something based on machine learning, that can work with nothing more than the pixels on the screen, as opposed to today's screen readers which have to be spoon-fed information through an accessibility API. Think OCR plus some kind of shape recognition to identify common UI elements.
That's an important point but lack of screen-readers (or whatever) support is forgivable at the early stages of a project that gives something conceptually interesting in exchange. I just hope they won't forget about it later when it gets clear that the project stabilizes and its future becomes clear.
Smalltalk has been around since the 1970s, though. Given the things the community is interested in, schools, video, sound, and so on, it's odd that accessibility never came up.
Most sighted persons really don't like thinking about blindness. It's not because they are bad people, it's just deeply scary and relatively uncommon.
This is an area where Apple really doesn't get enough credit. VoiceOver has absolutely revolutionized blind persons' ability to interact with consumer tech.
For more technically demanding applications, the Unix philosophy is inherently open to being made accessible. https://pjb.com.au/blin/.
As an aside, I personally really enjoy accessible websites, because they focus on clear communication in the presence of a serious obstacle, rather than assaulting the visual cortex with attention grabbing nonsense.
Edit: Also one could argue that ed(1)¹ is the pinnacle of accessibility.
Isn't it fascinating how posting a seemingly-useless comment that is guaranteed to be down-voted can provoke people to post interesting things in response? :-)
I just installed it on my WQHD ThinkPad running at 225% scaling and everything is blurry. It's definitely not running as a high-DPI-aware app. Are you saying there are some settings I can tweak to fix it? That would be great.
The TrackPoint response is all wrong too, but one step at a time... :-)
Pharo's code browser is visually compact. The code too. Current VR HMDs have only a small VGA-ish region of non-blurry pixels. Hmm...
Might be a good match. Emacs and IDEs with non-compact languages can be head-sloshing. And the HMD OEM market is being dysfunctional, so higher resolution is on the "I don't see a unicorn here - who cares about easily improving the world?" slowmo snail plan.
Neat. I (occasionally) use emacs in a trivial node.js-as-compositor stack on a Lenovo Explorer on linux. With a 7px font, a pixel-aligned window, no temporal supersampling (because low fps on an old laptop's integrated graphics), and WMR's handwave 500px-ish diameter pixels-not-blurred-together region, it's bearable, but far from competitive with a 1080p laptop screen. With subpixel rendering (Explorer has normal non-PenTile subpixels), a 5px font is ok (but not in emacs), but only across say 300px.
With better lenses and a better panel, an HMD might be competitive. But it's not perceived to be market, and there's pervasive confusion about the (minimal) GPU and (slow) fps needed. And there's only limited diy infrastructure.
But I enjoyed live-coding a vr environment (hot loading three.js and react.) At least with desktop mirroring, to flip-up to screen when the world broke.
Perhaps https://pharojs.github.io/ ? Croquet Project, Open Cobalt, and OpenQwaq are all dead, and 3dicc seems to have gone closed-source commercial. Ah, ST - feels like the 1980's all over again. :/ But https://github.com/ronsaldo/woden looks alive. And someone apparently hacked a bridge to Unreal. I wonder what else is out there?
Yayy! More mentioning of ST (and Pharo) on HN. I'm just a beginner, but am slowly learning. So far the IDE is the only one I really like as it is one with the language rather than an entirely separate entity.
The IDE and the repl-equivalent, along with the entire windowing system and the rest, are all just Smalltalk programs within your installation. So you use the tools to edit the tools.
It's basically an open-source, interpreted, pure OO operating system. You can make changes to the system's code while it's running, any part of the system, and changes will take effect immediately.
The "interpreted" bit isn't really true anymore. Smalltalk has been bytecode-compiled since, uh, I think 1976, and the bytecodes themselves are JITted by the Pharo/Squeak virtual machine these days. They're usually only interpreted when you're using the debugger.
"Scale aims to take Pharo into the shell. That is, to write shell scripts in Pharo, use its power, and have a better syntax instead of the ugly bash one"
There are a set of command line handlers that can be invoked and do eval or load code etc.
Serious question though, what is immersion in the programming context? Is it being only in the programming environment and not leaving? Wouldn't using a web browser for documentation or stack overflow be a break in it? Why not just have the windows one needs and configure your ide to be how you like?
> Is [immersion] being only in the programming environment and not leaving?
The dream is never leaving, as expressed by the Dynabook concept. However, in the full expression of that dream, the browser et al that you mention would also be inside the context.
In practice, things like using a browser for documentation is acceptable but not super common because viewing in the IDE is so much more powerful in certain ways (e.g. executable examples, finding "senders" of a message).
The idea is about having one's computer be "turtles all the way down" where you have the same power to change and explore every layer of the system without learning a whole new technology - from what we usually leave to the OS (which Dan Ingalls said "shouldn't exist") to what currently exists in apps (which Alan Kay and the creators of Smalltalk envisioned as services, which didn't stovepipe possibilities/power down to a small fraction of what's available).
I'm pretty new, but I've found a few ways. Most useful to modern development is a package called Iceberg, which is a git interface...you can export your classes/code/etc, commit them, and pull them into different images. You get to choose what you export, so you can only export your code.
There's also Monticello which is similar, but uses different repository types than git.
You can also just "File out" a class or package, which exports it to the file system.
Because objects in Smalltalk encapsulate their representation, it's not possible (without using reflection) to figure out exactly how an object represents itself. So things that would be primitives in other languages, like small integers, are in Smalltalk ordinary objects like anything else. The language works hard to present a 'uniform object model'.
However! When the rubber meets the road, there has to be some kind of bit-level representation somewhere. In Smalltalk, the VM hides these details from the programmer, who gets to reason uniformly about objects without caring about their representation. Specifically, Smalltalk VMs often use pointer tagging [0] just like lots of other dynamic languages do.
Or non-hypothetically, integers in Ruby -- 5.methods shows that 5 is an object just like anything else. But then Ruby has been called a modern Smalltalk for this reason.
Via compiler magic, conceptually you use all types the same way, but the compiler has specially cases for a couple of them, thus handling them as primitive types, even though you cannot tell the difference.
For example in Eiffel is even more complex, when you create a class it is possible to tag it with the default behaviour (reference or value), which you can later override when declaring variables or type parameters in generic code.
You're being downvoted because saying it's some imitation or reinvention of Lisp is a gross misrepresentation. Pharo's lineage takes it back to Smalltalk-80 beginning in 1976, at the very least.
If I remember correctly at least Alan Kay openly admits being very inspired by Lisp. Unfortunately the limitations/walled gardens they chose to impose in the name of simplification, everything is an object etc, had us running in circles for a long long time. My guess is it was picked up by managements for the same reasons as Cobol, Java, Go etc.
Well it doesn't say that. I just said it sounds like a reinvention of Lisp because that's what it sounded like to me from what it says on the homepage.
I'm not sure "trolling" is fair, but perhaps phrasing your confusion more as "the home page doesn't give me a very clear idea what this is," which is true; the actual front page doesn't mention Smalltalk at all. I suspect that they wrote it with the expectation that nobody just "ends up" on the Pharo web site without having some idea what it is already. Under normal circumstances (e.g., not being linked from the front page of HN) this is probably a reasonable assumption, but it couldn't hurt them to have something like what Squeak.org has: the first full sentence on its home page is "Squeak is an open-source Smalltalk programming system with fast execution environments for all major platforms."
Having said that, how few minutes qualify as "good"? Because if you click "About," that page has a heading that says "Pharo features" whose first bullet point reads "A dynamic, pure object-oriented programming language in the tradition of Smalltalk."
I like to think of each running instance as more akin to a Unix virtual machine than anything else. You end up using "in-image" version control systems like Monticello just the same way on a Unix server you use "in-operating-system" version control systems like git.