Jujutsu History Template


Jujutsu Version Control

Recently I’ve been using Jujutsu or jj for version control. It is, in effect, an alternate front-end to Git. There are any number of articles, tutorials and other online resources that can explain the finer details.

One feature I’ve been exploring more lately is Jujutsu’s ability to use templates to control how output is presented.

Compact history

I like a one-line history. jj provides that ability, but I was unhappy with the column order. Templates to the rescue. The following template shows the change id, then the commit id, the date and time, the author, and finally the description.

For the date and time I like to use the .ago() option to get relative time offsets. However the width of that information is variable, which ruins a nice table-like appearance. So I’ve opted for a 12-character format with a 24 hour clock.

The author on all of my own repositories is me, so having my name displayed on each line seems silly. The template compares the author email on the commit to my email address, and only displays the name if the two are different. Nice.

Here’s the template:

 
history = ["log", "-T", '''
  change_id.shortest(12)
  ++ " " ++ commit_id.shortest(12)
  ++ " " ++ committer.timestamp().format("%y-%m-%d %H:%M")
  ++ if(author.email() != "mark@zanshin.net",
      " " ++ author.email().local(),
      "",
    )
  ++ " " ++ if(description,
      description.first_line(),
      label("hint", "(no description set)"),
    )
''']

Put that into the [aliases] section of your configuration TOML. I have a bash alias setup to trigger this template. The results look like this:

Image of formatted jj log output:


Rust Language Immersion Course


Last November someone I follow on Mastodon posted about taking a week-long Rust programming course that used the Crafting Interpreters book as the project. At the I wished I could join in the fun.

A couple weeks ago she posted that the course was being offered again, for the last time. After thinking about it for a couple days I decided to sign up. It’s a five day course, Monday-Friday, from 9:30 am - 5:30 pm. The bulk of the time is spent crafting a rusty interpreter. The day starts with an overview of the next challenge in the book, and some ideas on how to best use Rust to solve them. Periodically through the day, there are more “lectures”.

I have definitely been immersed. I’m typically up by 6 or 6:30 am, so by the time the course day ends at 5:30 pm, I’ve put in a good 10 hours of work. I end up spending a couple more hours in the evening. I haven’t done this much focused programming in a long time.

Yesterday I spent the day creating the Abstract Syntax Tree (AST) portion of the project. Then in the evening I re-did most of it. My programming roots are procedural. The first dozen or so years of my career were spent writing COBOL. I’ve worked in Forte TOOL (a defunct 4Gl), and a smattering of scripting languages.

Where I am lacking is knowing about the helpers and syntactic sugar Rust provides. Wrapping my head around traits, or enums has taken some effort. My brute-force struct-for-every-statement approach worked, but it had far too much boilerplate code. Forcing my self to make use of enums and iterators made the code far more compact, and ultimately better.

That I have been programming for more then 45 years and I am still learning new tricks, new languages, and new ways to do things, is amazing. I was a mediocre student all through school. If you had told me back then that I would delight in learning new things, even esoteric things that I won’t ever use, for the joy of it, I wouldn’t have believed you.

I have book from the 1990s called “Thinking Body, Dancing Mind” (I think that’s the title). Being able to immerse myself in a week-long course, writing a programming language interpreter, in Rust, for fun is most certainly a case of dancing mind.


49 Years and Still Operating


The Voyager spacecraft are incredible. Nearly 49 years after launch and they are still functioning and still transmitting data. Simply astonishing engineering.


Crusty Interpreter


Several years ago I discovered Writing an Interpreter in Go and the sequel Writing a Compiler in Go, by Thorsten Ball. I managed to get most of the way through the first book. Thorsten did a superb job of explaining the concepts behind tokens, lexers, and parsing.

Several weeks ago Lindsey Kuper (@lindsey@resurse.social) talked about a week long class in creating an interpreter in Rust. The road map for this is Crafting Interpreters by Robert Nystrom.

A week or so ago, when she reposted a message from the creator and teacher behind the “Crusty Interpreter” course, I really wanted to take it. I am fascinated by Rust as a programming language, and I’m equally fascinated by interpreters and compilers. After pondering it for a while I decided to sign up.

Crusty Interpreter is a week-long, immersion style course. The short description for the course:

Challenge yourself by implementing an interpreter for a programming language that you don’t know (Lox) in a programming language that you might not know (Rust) from a book that uses a programming language that a lot of people used to know (Java).

Sounds like a blast. I didn’t take the operating systems course in college, so this is a change to catch up.

I likely won’t blog much about it during the week, but I hope to recap it once the course is over. Lindsey wrote about her experience in the course.


Greatest Car Review Ever



§

For any prime number, p, where p is greater than or equal to 5, p²-1 is always divisible by 24.


The War in Iran


This is a sobering, but essential read. The spreading ripples from this colossal error in judgment, are going to last for years, and they are going to alter the lives of everyone on the planet.


§

All governments suffer a recurring problem: Power attracts pathalogical personalities. It is not that power corrupts but that it is magnetic to the corruptible.
~ Frank Herbert


Tolkien's Hymn to Humility and Mercy


Seventy years ago, in January or February 1956, J.R.R. Tolkien wrote a letter to Michael Straight, editor of New Republic, in which he mentioned a “ferocious” letter he had received some time before. His correspondent had argued that Frodo, the protagonist of The Lord of the Rings, should have been executed as a traitor, not praised. “The ‘salvation’ of the world and Frodo’s own ‘salvation’," Tolkien replied, “is achieved by his previous pity and forgiveness of injury.”


Rust Namespace Pattern


Rust doesn’t provide namespaces, nor does it have static classes. If you want to group related functions under a named “thing”, without any actual data, a unit struct is the idiomatic way to accomplish this. Essentially the struct is being used as a logical container or namespace for the functions. The pattern is also known as a Unit Struct pattern, or a Zero-Sized Type (ZST) facade.

Here’s what it looks like:

pub struct Foo;

impl Foo {
    pub fn bar() -> i32 { 43 }
    pub fn baz() -> &'static str { "hello" }
}

You could just have free functions in a module, but the unit struct pattern has some specific advantages:

  • Trait implementation: You can implement traits on a unit struct. This is the primary reason for using this pattern. For example, you could implement a Handler, Strategy, or Factory trait on Foo. Something you can’t do on loose functions.

  • It can be passed as a value: Foo is a concrete type, so you can pass it to functions that expect a generic T bounded by some trait. Or you can use it in a collection.

  • It signals intent: Grouping methods on a struct signals that they are conceptually related in a way that’s more explicit than just a module.

The zero-sized part matters. Because Foo holds no data, it costs nothing at runtime. std::mem::size_of::<Foo>() is 0. The compiler may eliminate any instance of it, so it’s a pure compile-time organizational tool with no overhead.

A real world example using the marker/strategy pattern

pub struct JsonSerializer;
pub struct XmlSerializer;

trait Serializer {
    fn serialize(data: &str) -> String;
}

impl Serializer for JsonSerializer {
    fn serialize(data: &str) -> String { format!("{{\"data\": \"{}\"}}", data) }
}

impl Serializer for XmlSerializer {
    fn serialize(data: &str) -> String { format!("<data>{}</data>", data) }
}

Now you can write generic code over T: Serializer and swap implementations at compile time with zero runtime costs.

To summarize: a unit struct, also called a ZST namespace/facade pattern, is used for trait implementation, type-level polymorphism, and logical grouping. All with zero runtime cost.