Paolo Amoroso's Journal

Tech projects, hobby programming, and geeky thoughts of Paolo Amoroso

I'm writing this on my new desktop computer.

It's a Linux system I chose after deciding to migrate from ChromeOS back to Linux: a System76 Merkaat short case mini PC with a 5 GHz 13th gen Intel Core i7 processor, Intel Iris Xe graphics, 32 GB RAM, 500 GB SSD, 2.5 G Ethernet, and Wi-Fi 6. It replaces an ASUS Chromebox 3 I used since 2018.

I installed Linux Mint 21.3 Cinnamon Edition, configured the operating system, and downloaded the main programs I need. This is my experience after a week of usage.

In short, I absolutely love the Markaat and Mint which both exceed my expectations.

Hardware

The Merkaat case has a similar footprint and convenient port locations as the Chromebox but is noticeably smaller. This is the Merkaat connected via USB-C to a VOTNUT USB speaker.

System76 Merkaat mini PC with a USB speaker.

A 23” HP Pavillon 23cw LCD screen, a wireless TedGen keyboard, a wireless Logitech M220 mouse, and a Logitech c920 webcam round up the hardware setup on a desk next to the bookcase where the Merkaat is. A Brother HL-L2340DW wireless laser printer sits nearby.

System76 Merkaat Mini PC along with its screen, keyboard, and mouse.

At the highest spin rate the Merkaat fan is quieter than the Chromebox one at the lowest. Speaking of cooling, the case doesn't go much past slightly warm.

Software

In the first week with Mint I discovered many little Linux features and customization options I didn't know I wanted until I escaped the confines of ChromeOS. They make a difference in everyday work and reduce friction.

I use Cinnamon, the flagship desktop environment of Mint. I had no prior exposure to it but I like its pleasant design, features, and coherence. The environment strikes a good balance between ease of use and customizability. But aside from adding a few program icons to the panel as in the screenshot, so far I haven't customized Cinnamon much.

Screenshot of the Linux Mint desktop with the panel menu open.

It's still early to comment on the stability of Linux, especially with system updates and upgrades to major versions of the distro, and it's something I'll monitor. But Mint has been working smoothly over the first week.

Usage

The new system is fading into the background.

In everyday use it's getting easier to focus on the task at hand without thinking much to how to accomplish it on Linux. This is all I can ask of a system: supporting my work and getting out of the way.

I'm also unlearning almost a decade of ChromeOS muscle memory and workflows, and relearning the Linux way.

Setup and configuration

Installing Mint was uneventful, mostly.

I flashed the Mint USB boot stick on the Chromebox using the ChromeOS Recovery Utility. But when I booted the stick the Merkaat displayed a long series of scary looking errors, as the ISO was somehow corrupted or incorrectly encoded.

I had to start Pop!_OS preinstalled on the Merkaat and flash the stick again. Booting Mint from the reflashed stick finally led me through the few simple steps of the installation flow.

The installer allowed me to set different languages for the Linux user interface and locale. I want the Italian locale, as I live in Italy, but the English user interface, which comes more natural for tech stuff. Nice, I don't remember it being so easy when I last installed Linux a dozen years ago.

Configuring the system also involved installing the System76 Driver required for sound and other hardware support.

I worried Linux wouldn't fully detect my hardware or not work well with it. But you know who malfunctioned? Yours truly.

At first I didn't realize the pairing of my Mixcoder E9 Bluetooth headphones completed successfully and thought something was off. Also, no sound coming from the USB speaker had me puzzled until I figured the volume was turned all the way down. Doh.

Mint actually detects and fully supports all the hardware of the Merkaat and the connected peripherals including audio, Bluetooth, Wi-Fi, and the printer.

I turned on the printer and, while figuring how to run the Mint print manager, a notification informed the unit was added. This is pure magic.

Performance

The 13th gen Intel Core i7 processor of the Merkaat is five generations newer than the 8th gen of the Chromebox and, along with double the RAM, the upgrade can be felt. The machine is snappy and responsive. I no longer observe the subtle but perceivable lag associated with most actions on the Chromebox.

The system takes slightly longer to boot than the Chromebox but still under twenty seconds.

On average, with comparable workloads Linux consumes a few GB less RAM than ChromeOS. As I write this about 5 GB of RAM are in use and around 3 GB of cache, out of 32 GB.

Firefox is now my primary browser. For some tasks or actions Firefox seems slightly slower than than Chrome, but still perfectly useable and more than reactive.

Community

For researching, setting up, and configuring the new system I relied on three main sources of information and support: the Linux Mint Forums, Mastodon, and System76. From all the sources I received prompt, informative, and helpful answers to my questions and requests for assistance.

The community is a valuable feature of Mint and Linux and I hope to give back in some way.

Conclusion

Using the Merkaat for a week drove home how much I missed out over the past decade.

I'm really happy I finally found the motivation and momentum to leave ChromeOS for Linux. The Merkaat and Linux rewarded my decision with smooth sailing.

It feels good to be back home.

#Linux #ChromeOS

Discuss... Email | Reply @amoroso@fosstodon.org

I'm going back to Linux after nine years of ChromeOS.

In 2015 I had been using Linux for a couple of decades. Exasperated by the frequent maintenance issues, that year I migrated to ChromeOS. I was living in the browser anyway and ChromeOS seemed like a stable alternative, so I switched. Linux system updates often broke the Nvidia drivers or X11 and dumped me to the text console with no clue what to do. At some point I even stopped performing updates.

Almost a decade later, something changed and made me want to leave ChromeOS for Linux. This post records the motivations for the switch, lists my requirements, and describes the hardware and software setup I came up with for my new Linux system.

Motivation

ChromeOS served me well. It fulfilled my needs, nearly eliminated maintenance, and made upgrading to new devices as simple as signing into an account.

However, in all these years Linux has come a long way in features and usability. Getting a Raspberry Pi 400 gave me a glimpse at how much Linux improved.

These are not the only reasons for migrating though.

Google is considering for ChromeOS a feature similar to Windows Recall. Which is a deal breaker.

Although Google may implement the feature in a thoughtful way, it bothers me not just in itself but for the trend it hints at. I'm not interested in the wave of new AI features the market is pressuring tech companies to deliver.

Another motivation for moving to Linux is a consequence of how my use of ChromeOS changed.

Although I always relied on cloud applications, for the past few years I've been running more Linux programs under the Crostini Linux container of ChromeOS, such as emulators and specialized tools. And I've been increasingly bumping into subtle limitations of the Crostini environment like GUI rendering and keyboard issues, missing features, and minor incompatibilities here and there.

The solution is to move to a full, native Linux system.

The requirements

My experience with Chrome and Crostini on ChromeOS shaped the requirements for the new Linux system.

Hardware

My ChromeOS daily drivers have been Chromeboxes, the latest of which an ASUS Chromebox 3 since 2018. The device has an 8th gen Intel Core i7 processor, an Intel graphics chip, 16 GB RAM, 256 GB of storage, and 1 G Ethernet.

With their compact size and easy access to all ports at desk level, Chromeboxes made me fall in love with the mini PC form factor. The new machine must be a mini PC too.

The performance of the ASUS Chromebox 3 matches well my needs based on a mix of web apps and Linux software. The most graphically demanding programs I run are astronomy applications like Celestia and Stellarium or tools like basic video editors. The graphics chip of the ASUS Chromebox 3 makes them run well with no noticeable lag or stutter.

My typical workload consists of a dozen Chrome tabs plus one or two Linux programs. This computing mix usually fills half a dozen GB of RAM out of 16 GB. The local files take up about 160 GB of the available 256 GB storage.

I'm not a gamer and don't need a gaming rig. I don't plan to do machine learning either. However, I want extra room in the specs to accommodate some growth in my computing needs.

An updated Intel Core i7 processor and double the current RAM and storage seem adequate. The hardware should have good Linux compatibility, which doesn't mesh well with Nvidia chips. I don't need all that horsepower anyway as an Intel graphics chip is perfectly adequate.

Software

Again, my ChromeOS experience guided also my software requirements. I don't run servers and typically use web applications and Linux programs, so most Linux distros would do. However, I have a few extra requirements for a better setup and maintenance experience.

I want a desktop distro that's easy to install and maintain. It should be popular, actively maintained, and not controlled by a corporation.

On the technical side, my ideal distro should support a wide range of hardware and peripherals. Since most of the programs I need are available as .deb packages the distro must be based on or derived from Debian, default to APT, and not force alternate package managers like Snap. In addition, the distro should provide common software and support for installing multimedia codecs without jumping through hoops.

Although I'd appreciate a distro ready for Wayland, I actually have no immediate need for Wayland's features. I'd just like to put the transition from X11 behind me.

I never used Android apps much, which few developers bothered to adapt to ChromeOS anyway. I won't miss them on Linux.

The setup

Researching the right hardware and software combination was harder than anticipated because of two main reasons. First, I have been out of the Linux and PC loop for so long I'm not much familiar with the latest Linux features, desktop environments, and PC hardware.

Searching the web to make sense of the computer market and the huge selection of mini PCs was equally hard. Most keywords associated with the relevant queries are so high traffic the search result pages are filled with ads, SEO-optimized content, and noise.

I mostly skipped googling and went with asking around and browsing through the product listings of manufacturers I already know, who design for Linux, or are recommended by trusted sources.

A lot of reading and thinking later, I came up with a setup I'm pleased with.

Hardware

My new daily driver is a System76 Merkaat mini PC with a short case, a 13th gen 5 GHz Intel Core i7 processor, Intel Iris Xe graphics, 32 GB RAM, 500 GB SSD, 2.5 G Ethernet, and Wi-Fi 6.

The machine has two killer features. It's designed for Linux and should have little or no compatibility issues. Also, System76 let me configure the product with exactly the specs and features I wanted: processor, RAM size, storage size, and so on. The one size fits all models by other manufacturers typically miss one or more of the features I want.

I need no additional peripherals as I'll use those of the Chromebox: a 23” HP Pavillon 23cw monitor, a wireless TedGen keyboard, a wireless Logitech M220 mouse, a Logitech c920 webcam, a Blue Yeti microphone, and a Brother HL-L2340DW printer.

The only downside is System76 is an American company and I live in Italy, so dealing with import taxes and duties will be a bloodbath. But I hope the investment will pay off.

Software

Linux Mint is the distro that ticks all my boxes and I'll install on the System76 Merkaat.

It's popular, easy to use, low maintenance, and with no known incompatibilities with System76 devices. It comes with a lot of software out of the box, including multimedia codecs.

A non technical feature that impressed me is Mint is a community distro, which makes the interests of users align with those of developers. Making Snap optional drove this home.

Wayland is optional and experimental on Mint. Hopefully, when Wayland is ready, Mint will provide a smooth upgrade path.

What's next

I ordered the System76 Merkaat and the device is on its way to me. It should land here in a week or so. When I set up the system and play with it a bit, I'll share my impressions.

In the meantime, please excuse me while I refresh the tracking status of the shipment.

#Linux #ChromeOS

Discuss... Email | Reply @amoroso@fosstodon.org

I implemented the last features originally planned for Insphex, my hex dump tool in Common Lisp for Medley Interlisp.

The first new feature is an Exec command for invoking the program. The command HD works the same way as the function INSPHEX:HEXDUMP and accepts the same arguments, a file name and an optional boolean flag to indicate whether the output should go to a separate window:

← HD FILENAME [NEWIN-P]

The other feature is the addition to the File Browser menu of the Hexdump command, which shows the hex dump of the selected files in as many separate windows:

Hexdump File Browser command of the Insphex hex dump tool on Medley Interlisp.

For other commands that produce output in windows the File Browser lets the user view one window at a time, with menu options for skipping through the windows. Insphex doesn't do anything so elaborate though.

Implementing the features was easy as the relevant Interlisp APIs are well documented and I have experience with adding an Exec command to Stringscope.

The Medley Lisp library modules manual covers the File Browser API from page 115 of the PDF, with the explanation of how to add commands on page 118. It's as simple as registering a callback function the command invokes, INSPHEX::FB-HEXDUMP for Insphex.

An issue I bumped into is that instead of 4 arguments as the manual says, the callback actually requires 5. The last, undocumented argument was likely introduced since the publication of the manual.

#insphex #CommonLisp #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I added a GUI to Insphex, the hex dump tool I'm writing in Common Lisp on the Medley Interlisp environment.

The initial version printed the hex dump only to the standard output, now optionally to a separate TEdit window with a command menu. The menu has items for displaying the next page of output, redisplaying from the beginning of the file, and exiting the program.

Window and command menu of the Insphex hex dump tool for Medley Interlisp.

Most window, menu, and other Medley GUI facilities, like the TEdit rich text editor, provide Interlisp APIs in the IL package that Common Lisp programs such as Insphex can access. However, since the APIs usually rely on Interlisp records, from Common Lisp it's often necessary to write quite a few package qualifiers like this example to create a menu record:

(IL:CREATE IL:MENU
           IL:ITEMS IL:← '(ITEM1 ITEM2 ITEM3)
           IL:MENUFONT IL:← '(IL:MODERN 12)
           IL:TITLE IL:← "Menu"
           IL:CENTERFLG IL:← T)

The XCL:DEFINE-RECORD macro helps reduce package qualifiers by wrapping Interlisp records in equivalent Common Lisp structures with ordinary structure accessors, setters, predicates, and constructors. The structures can be in any package, not just IL like Interlisp symbols. XCL:DEFINE-RECORD is described on page 7-3 (page 143 of the PDF) of the Medley 1.0 release notes.

This way Common Lisp blends well with Interlisp and reduces verbosity. For example, this is the Insphex Common Lisp function that creates the output window:

(DEFUN CREATE-HEX-WINDOW (FILE)
   "Create and return a window to display the hex dump of FILE."
   (LET* ((IN (OPEN FILE :DIRECTION :INPUT :ELEMENT-TYPE '(UNSIGNED-BYTE 8)))
          (COMMANDS (IL:MENUWINDOW (MAKE-MENU :ITEMS '(("Next" :NEXT "Show the next page.")
                                                       ("Reread" :REREAD "Reread the input file.")
                                                       ("Exit" :EXIT "Quit the program."))
                                          :MENUFONT
                                          '(IL:MODERN 12)
                                          :TITLE "Commands" :CENTERFLG T :WHENSELECTEDFN 
                                          #'HANDLE-MENU)))
          (OUT (IL:OPENTEXTSTREAM))
          (TEDIT-PROC (IL:TEDIT OUT))
          (WINDOW (IL:WFROMDS OUT)))
     (IL:ATTACHWINDOW COMMANDS WINDOW 'IL:TOP 'IL:LEFT)
     (IL:WINDOWPROP WINDOW 'INSTREAM IN)
     (IL:WINDOWPROP WINDOW 'OUTSTREAM OUT)
     (IL:WINDOWPROP WINDOW 'BLOCK-OFFSET 0)
     (IL:WINDOWPROP WINDOW 'IL:TITLE (FORMAT NIL "Insphex ~A" FILE))
     (NEXT-HEX-PAGE WINDOW)
     WINDOW))

The INSPHEX::MAKE-MENU constructor creates a Common Lisp INSPHEX::MENU structure that wraps the Interlisp IL:MENU record.

Most of the Insphex GUI functionality is in place but I need to work on a couple of tweaks.

First, the Insphex window should be read-only whereas now the user can type into the editor buffer. Next, I need to clean up all the allocated resources when the user quits the program via various interaction flows, such as closing the window instead of clicking the Exit menu item.

#insphex #CommonLisp #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I'm developing the new program Insphex (inspect hex), a hex dump tool that is created with and runs on the Medley Interlisp environment.

Similarly to the Linux command hexdump, it shows the contents of files as hexadecimal values and the corresponding ASCII characters. An early version of the program prints the hex dump to the standard output like this.

Output of the Insphex hex dump tool for Medley Interlisp.

I plan to enhance Insphex to optionally display the dump in a separate window one page at a time. An attached menu will have options for showing the next page and exiting. I'll also provide an Exec command for running the program.

The code is in Common Lisp but will include some Interlisp to access the required system functionality.

Although Insphex is useful in itself, I have three main goals for it. First, I want a real project to practice the process for writing Common Lisp with the residential environment of Medley. This is the native way of coding on Medley and takes full advantage of its development environment and features such as the File Manager and the SEdit editor.

Most Medley tools and facilities are written in Interlisp or expose Interlisp APIs through which the functionality can be invoked. So another goal is to interface with Interlisp from Common Lisp to access the functionality I need like windows and menus.

My third goal is to experiment with displaying textual output in TEdit, the Medley word processor where the hex dump will optionally go.

Although the Interlisp API of TEdit supports advanced editing and formatting, Insphex does only basic text output. The primary feature I want is TEdit's ability to automatically handle repainting the window after it's resized or a hidden portion is exposed. This is handy as by default Interlisp windows mostly don't handle the repaint.

Now that the basic functionality of Insphex is in place I will implement displaying the hex dump in a TEdit window.

#insphex #CommonLisp #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

When the Medley Interlisp Project began reviving the system around 2020, its Common Lisp implementation was in the state it had when commercial development petered off in the 1990s, mostly prior to the ANSI standard.

Back then Medley Common Lisp mostly supported CLtL1 plus CLOS and the condition system. Some patches submitted several years later to bring the language closer to CLtL2 needed review and integration.

Aside from these general areas there was no detailed information on what Medley missed or differed from ANSI Common Lisp.

In late 2021 Larry Masinter proposed to evaluate the ANSI compatibility of Medley Common Lisp by running the code of popular Common Lisp books and documenting any divergences. In March of 2024 I set to work to test the code of the book Practical Common Lisp by Peter Seibel.

I went over the book chapter by chapter and completed a first pass, documenting the effort in a GitHub issue and a series of discussion posts. In addition I updated a running list of divergences from ANSI Common Lisp.

Methodology

Part of the code of the book is contained in the examples in the text and the rest in the downloadable source files, which constitute some more substantial projects.

To test the code on Medley I evaluated the definitions and expressions at a Xerox Common Lisp Exec, noting any errors or differences from the expected outcomes. When relevant source files were available I loaded them prior to evaluating the test expressions so that any required definitions and dependencies were present. ASDF hasn't been ported to Medley, so I loaded the files manually.

Adapting the code

Before running the code I had to apply a number of changes. I filled in any missing function and class definitions the book leaves out as incidental to the exposition. This also involved adding appropriate function calls and object instantiations to exercise the definitions or produce the expected output.

The source files of the book needed adaptation too due to the way Medley handles pure Common Lisp files.

Skipped code

The text and source files contain also code I couldn't run because some features are known to be missing from Medley, or key dependencies can't be fulfilled. For example, a few chapters rely on the AllegroServe HTTP server which doesn't run on Medley. Although Medley does have a XNS network stack, providing the TCP/IP network functions AllegroServe assumes would be a major project.

Some chapters depend on code in earlier chapters that uses features not available in Medley Common Lisp, so I had to skip those too.

Findings

Having completed the first pass over Practical Common Lisp, my initial impression is Medley's implementation of Common Lisp is capable and extensive. It can run with minor or no changes code that uses most basic and intermediate Common Lisp features.

The majority of the code I tried ran as expected. However, this work did reveal significant gaps and divergences from ANSI.

To account for the residential environment and other peculiarities of Medley, packages need to be defined in a specific way. For example, some common defpackage keyword arguments differ from ANSI. Also, uppercase strings seem to work better than keywords as package designators.

As for the gaps the loop iteration macro, symbol-macrolet, the #p reader macro, and other features turned out to be missing or not work.

While the incompatibilities with ANSI Common Lisp are relativaly easy to address or work around, what new users may find more difficult is understanding and using the residential environment of Medley.

Bringing Medley closer to ANSI Common Lisp

To plug the gaps this project uncovered Larry ported or implemented some of the missing features and fixed a few issues.

He ported a loop implementation which he's enhancing to add missing functionality like iterating over hash tables. Iterating over packages, which loop lacks at this time, is trickier. More work went into adding #p and an experimental symbol-macrolet.

Reviewing and merging the CLtL2 patches is still an open issue, a major project that involves substantial effort.

Future work and conclusion

When the new features are ready I'll do a second pass to check if more of the skipped code runs. Another outcome of the work may be the beginning of a test suite for Medley Common Lisp.

Regardless of the limitations, what the project highlighted is Medley is ready as a development environment for writing new Common Lisp code, or porting libraries and applications of small to medium complexity.

#CommonLisp #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I read the book Recursion via Pascal by Jeffrey S. Rohl, Cambridge University Press, 1984. I discovered this rare, little known gem by chance and, although it's available online, I also bought a cheap printed copy.

The book Recursion via Pascal.

What makes this short book so interesting? The preface opens with this sentence:

Recursion is the cinderella of programming techniques where languages such as Pascal are concerned.

Programming books usually devote little space to recursion, often to briefly introduce and dismiss it as hard to understand and inefficient.

As a Lisp enthusiast, recursion comes natural to me and is a natural fit for the language. A work entirely on recursion like this is the book I always wanted but didn't know existed.

Recursion via Pascal uses math throughout the text but the explanations are usually clear, and the elementary discrete mathematics with a few bits of calculus is not hard to follow. The snippets in Pascal are short and mostly easy to understand. This code can be ported or adapted with little effort.

What makes the book unique is the coverage of recursion techniques. For example the techniques for making recursion more efficient such as terminating recursive calls earlier, or implementing algorithms with auxiliary procedures that take fewer parameters. The book also gives guidance on when recursion is most appropriate and when it's not.

While most programming books don't say much on eliminating recursion other than remarking it's easy, Recursion via Pascal has a full chapter on this that actually explains what to do.

I'm glad to have such a handy reference work on recursion.

#books #pascal #development

Discuss... Email | Reply @amoroso@fosstodon.org

I finally fixed a longstanding duplicate definitions issue with Femtounit, my Interlisp unit test framework.

Femtounit creates a new File Manager type for unit tests, TESTS. DEFTEST, which defines a test, adds an entry of type TESTS and expands into an internal function that carries out the test when called. The problem was the File Manager noticed and tracked two objects for each DEFTEST, the TESTS entry and the function. But the function is an implementation detail that shouldn't be tracked by the File Manager or seen directly by the user.

To fix the issue, in the DEFDEFINER that creates the new type I added a call to UNMARKASCHANGED immediately after the definition of the internal function. UNMARKASCHANGED undoes the association with the File Manager the creation of the internal function establishes. I had tried DELDEF but it removes both the association and the internal function.

I also refactored the definition of the internal function to use DEFINEQ instead of assigning a LAMBDA to the function cell of the symbol naming the test, which is less clear and obscures the intent.

#femtounit #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I shut down Free Python Books, the list of Python books that are free to read online or download I had been maintaining since 2019.

I archived the project's GitHub repo, which is now read-only and no longer accepts contributions. If you're interested you're welcome to fork the repo and maintain your copy.

When learning Python I came across many great free Python books, so I started keeping track of them. In early 2019 I shared my list on Reddit where it resonated with many. I later published the list on GitHub and it eventually gained over 4,500 stars, about 600 forks, and over 100 watchers.

In 2023 I rediscovered my love of Lisp and lost interest in Python, which I no longer use. Hence my decision to mothball the project.

Thanks to all who expressed appreciation or contributed, and to the Python community for producing so many great works.

#Python #books

Discuss... Email | Reply @amoroso@fosstodon.org

I got a cheap used copy of the book A Programmer's Guide to COMMON LISP by Deborah G. Tatar, Digital Press, 1987.

The book A Programmer's Guide to COMMON LISP by Deborah G.

Why did I read such an old book, published a few years after CLtL1 and well before ANSI finalized the Common Lisp standard?

I'm always looking for good Lisp books. Since Medley is my primary Lisp environment, I'm particularly interested in books published when the system was originally developed and used. These works are relevant because they cover a set of features close to the state of the Common Lisp implementation of Medley, and present a programming style typical of Lisp development in those years.

Two old reviews got me curious about A Programmer's Guide to COMMON LISP, one by Daniel Weinreb and the other by Richard Caruana.

Both reviews point out the book is different from most contemporary introductory Lisp books which focus on AI. Although Tatar's does contain some AI code, such as an interesting and complete toy expert system, the sample code spans a wider range of domains like a text formatter similar to nroff.

What sets A Programmer's Guide to COMMON LISP apart from other Lisp books is its environment independent discussion of the interactive Lisp programming process. Writing code in the editor, evaluating expressions from the editor, interacting with the REPL for testing expressions and exploring, and so on.

I've never seen the process expressed so clearly in any book, past of present. I'm familiar with it but the material is particulary helpful for complete beginners.

Although the short chapter on macros presents some interesting examples like a simplified version of defstruct, it doesn't discuss gensym and variable capture. This is unusual. But it's only one of a few issues and the book is a valuable addition to my Lisp library.

#CommonLisp #books #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

Enter your email to subscribe to updates.