UCL: Some Updates

Made a few minor changes to UCL. Well, actually, I made one large change. I’ve renamed the foreach builtin to for.

I was originally planning to have a for loop that worked much like other languages: you have a variable, a start value, and an end value, and you’d just iterate over the loop until you reach the end. I don’t know how this would’ve looked, but I imagined something like this:

for x 0 10 {
    echo $x
}
# numbers 0..9 would be printed.

But this became redundant after adding the seq builtin:

foreach (seq 10) { |x|
    echo $x
}

This was in addition to all the other useful things you could do with the foreach loop1, such as loop over lists and hashes, and consume values from iterators. It’s already a pretty versatile loop. So I elected to go the Python way and just made it so that the for loop is the loop to use to iterate over collections.

This left an opening for a loop that dealt with guards, so I also added the while loop. Again, much like most languages, this loop would iterate over a block until the guard becomes false:

set x 0
while (lt $x 5) {
    echo $x
    set x (add $x 1)
}
echo "done"

Unlike the for loop, this is unusable in a pipeline (well, unless it’s the first component). I was considering having the loop return the result of the guard when it terminates, but I realised that would be either false, nil, or anything else that was “falsy.” So I just have the loop return nil. That said, you can break from this loop, and if the break call had a value, that would be used as the result of the loop:

set x 0
while (lt $x 5) {
    set x (add $x 1)
    if (ge $x 3) {
        break "Ahh"
    }
} | echo " was the break"

The guard is optional, and if left out, the while loop will iterate for ever.

The Set! Builtin

Many of these changes come from using of UCL for my job, and one thing I found myself doing recently is writing a bunch of migration scripts. This needed to get data from a database, which may or may not be present. If it’s not, I want the script to fail immediately so I can check my assumptions. This usually results in constructs like the following:

set planID (ls-plans | first { |p| eq $p "Plan Name" } | index ID)
if (not $planID) {
    error "cannot find plan"
}

And yeah, adding the if block is fine β€” I do it all the time when writing Go β€” but it would be nice to assert this when you’re trying to set the variable, for no reason other than the fact that you’re thinking about nullability while writing the expression to fetch the data.

So one other change I made was add the set! builtin. This will basically set the variable only if the expression is not nil. Otherwise, it will raise an error.

set! planID (ls-plans | first { |p| eq $p "Missing Plan" } | index ID)
# refusing to set! `planID` to nil value

This does mean that ! and ? are now valid characters to appear in identifiers, just like Ruby. I haven’t decided whether I want to start following the Ruby convention of question marks indicating a predicate or bangs indicating a mutation. Not sure that’s going to work out now, given that the bang is being used here to assert non-nullability. In either case, could be useful in the future.


  1. And the seq builtin ↩︎

Enjoyed this week’s P&B with Lou Plummer. I enjoy reading linkage.lol and I know people I work with who also follow’s Lou’s work. His blog recommendations look interesting too.

Released a new plugin for Micro.blog: Sidebar for the Bayou theme (yes, another sidebar plugin). Thanks again to @Mtt for making changes to the theme to support adding the sidebar. Can be installed from the plugin directory (please ensure you have Bayou version 1.1.3 or later).

What a cruel irony it is that the instinctive response to an itchy eye is to rub it, which doesn’t provide relief and only prolongs the irritation. The only way out is to ignore instinct and practice self control. There’s a metaphor here somewhere I’m sure.

About My New Cooler's Programming Feature

There’s lots to like about my new cooler, but the programming feature is not one of them. My old unit had a very simple timer with two modes: turn cooler on after N hours, or turn cooler off after N hours. Anything else requires manual intervention.

Auto-generated description: A wall-mounted thermostat with buttons labeled for power, mode, and temperature adjustment options.
The old control panel (turns out I did have a photo, albeit an old one). Set the mode: cool/vent (fan), the power setting, then tap Timer Select to choose between turn on or off after N hours.

I really liked this simple setup. Many times in summer, when the days are warm but not hot, and the nights are cool, it was nice to turn the cooler’s fan on and set the timer to turn it off after 2 to 3 hours, maybe longer if the days were a bit warmer1. The cooler will simply pull cool air from outside and circulate it around the room. This was enough for me to get to sleep, at which point the cooler would shut itself off in the middle of the night.

With the new cooler comes a control panel that is effectively a cheap Android phone, so it’s capable of much more. You can now set a program that has four separate modes set to four separate times of the day. Each day of the week now has it’s own program too. A particular mode can either be a desired temperature setting, or “off”.

Auto-generated description: A digital control panel for a Seeley International air conditioning system displays a program schedule with times and statuses.
The new control panel, showing the four mode settings for a particular day of the week.

To recreate what I previously had, I would now have to choose a specific shutoff time for every day of the week. No longer am I able to set the running time based on how I feel: it has to be an actual timestamp, with several taps involve if you want to change it. This timestamp can only be set up to 11:59 PM so if you want the unit to shut off after midnight, you’ll have to remember to choose the program for the next day.

Oh, and mercy on you if you wanted a timestamp that didn’t land on the hour. The minutes can only be changed by 1, so you’ll be tapping 30 times if you want the unit to shut-off at the half hour.

Auto-generated description: A digital thermostat screen displays settings including start time, day selection, temperature, and status options.
Adjusting the settings of a particular mode, one minute at a time.

You also have no control over the fan speed. That was another nice thing about the old unit: you set the speed to what you want, and then you set the timer. The unit will stay in that mode until it shuts off. I don’t want the fan to be blowing a gale when I’m trying to get to sleep, so the fan was usually set to the lowest or second-lowest setting.

This new programming modes only have a temperature setting, so if the house is warm, the cooler will crank up the fan until it reaches half-speed or just above; speeds I usually use in the middle of a very hot day. This means noise that will change in intensity as the target temperature is reached. I’m not a great sleeper so any additional noise like that is disruptive.

Auto-generated description: A Seeley International MagiQtouch controller displays temperature settings and a schedule for cooling with the message PREWET IN PROGRESS.
The unit operating in program mode.

So I’m a little sad that I lost this simple timer-based approach to operating the cooler. I’m not even sure who this programming feature is built for. It sort of exists in that nether region where it’s too complicated for the simple things, yet useless for anything other than a set weekly routine. I set my cooler based on the weather conditions which, you may be surprised to know, does not fall into a nice weekly routine. Granted, it may make it possible to use this to recreate the simple timer approach I had before: I just preset everything and only activate the program when I want it. And yeah, it’ll probably be fine, but I do feel like I’ve lost something.

Update: Apparently the cooler does have a shutoff after N hours feature. It’s just buried in the settings menu. The post still stands, as it would’ve been nice that this was a feature of the Program mode, but at least there’s something I can use.


  1. If the days and nights are hot, I don’t bother with the timer and just leave it running all night long. ↩︎

Recent earworm: Samplextra, by Lee Rosevere. 🎡

Quick review of Samplextra, by Lee Rosevere. Rating: good. Review: I’ve only recently discovered this album a couple of weeks ago, and although many of the tracks are yet to grow on me, the 1st and 3rd are great.

I don’t understand YouTubers who start their videos with a hype bumper. Just get to the frickin’ interview. You got my attention already.

Keep forgetting to give my keep-cup a proper clean when I get home. Now today’s morning coffee taste like yesterday afternoon’s lemon and ginger tea.

I had a Ventolin inhaler expire on me, a first in my life. It was a little smokey yesterday so I used it, and sprayed… something into my lungs, resulting in a coughing fit. I still feel the effects a bit today. Hope I didn’t cause too much damage.

πŸ”— Animating Rick and Morty One Pixel at a Time

Using OpenGL Shading Language, which is apparently supported by browsers, to produce an animation of Rick from Rick and Morty. I’ve yet to go into this post in any great detail, but it certainly looks very interesting.

Via: Simon Willison

My unread items in Feedbin are starting to pile up again, largely because I’m “keeping them for later.” Need to decide when later is now. May as well have later be now now. 🧐

May have gotten to the bottom of a problem that was stumping people at work. The cause, yet to be verified, looks to be a change in the integer value of a gRPC enum value. Suspect that it may have been a manual change to generated code (yeah, try to avoid doing that if you can help it).

It’s a shame online integrations assumes that everyone’s using GitHub. I can understand why they build their products that way, and I knew that I’d loose much of those integration niceties when I moved to my own setup. Still, it would be nice to see more integrations work with any Git-based SCM.

Trying out Bayou theme by @Mtt on a test blog. Lots to like about it, especially the idea of having the latest micro-post appear in the form of a status message. Very unique.

Auto-generated description: A minimal blog page titled Leon Mika features categories like About, Archive, and Replies, along with a post mentioning categories being sorted and three other listed posts from 4 February 2025.

New AC installed and doing the best it can on this slightly humid day, although it’s performing better than the old unit. I forgot to take a photo of the old panel, so here’s a photo of the new one (the lower one is for the heater).

A digital thermostat showing a temperature of 25Β°C is mounted below another digital control panel with a touchscreen showing various settings displayed.

Already making daily note archives for 2025.

Auto-generated description: A computer interface displaying a folder structure for daily notes from 2022 to 2025, highlighting a note dated January 6, 2025.

Was not expecting the 10 mm of rain we had last night. Not sure anyone was, not even the Bureau. Put a dampener on my plans. I thought I was being super smart washing my towels in the evening and hanging them on the line to dry overnight so I could use the line for other washing today. Ah well. 🀷

Idea for UCL: Methods

I’m toying with the idea of adding methods to UCL. This will be similar to the methods that exist in Lua, in that they’re essentially functions that pass in the receiver as the first argument, although methods would only be definable by the native layer for the first version.

Much like Lua though, methods would be invokable using the : “pair” operator.

strs:to-upper "Hello"
--> HELLO

The idea is to make some of these methods on the types themselves, allowing their use on literals and the result of pipelines, as well as variables:

set h "Hello"
$hello:to-upper
--> HELLO

"Hello":to-upper
--> HELLO

(cat "Hello " "world"):to-upper
--> HELLO WORLD

The use of methods would be purely conveience. One could conceive of a type, like a CSV table, where there’s a need to perform a series of operations over it.

The potential downside of using : is that it may make defining dictionaries more ambiguous. When the parser sees something that could either be a list or a dictionary, it does a scan to search for any pairs that may exist. If so, it treats the literal as a dictionary. But would this work with using : as the method separator?

["Hello":to-upper]
--> Is this a dictionary {"Hello":"to-upper"}, or the list ["HELLO"]?

So that would require something. Might be that I need to require method invocations to occur within parenthesis for list literals.

Ambiguities like this aside, I’m planning to keep it simple for now. Methods will not be user definable within UCL out of the gate, but would be effectively another interface available to native types. For the builtin ones that exist now, it’ll most likely be little more than syntactic sugar over the standard library functions:

$hello:to-upper

# equivalent to

strs:to-upper $hello

Speaking of the standard library, the use of : as a module-function separator may need some changes. A the moment it’s a bit of a hack: when a module is loaded, any procs defined within that module is stored in the environment with the operator: strs:to-upper for example. The parser is sophisticated enough to recognised : as a token, but when it parses two or more identifiers separated with :, it just joins them up into a single identifier.

What’s needed is something else, maybe using something based on methods. The current idea is to define modules as being some special form of object, where the “methods” are simply the names of the exported symbol.

I was curious to know whether the language was capable of doing something similar now. It’s conceivable that a similar concept could be drafted with procs returning dictionaries of procs, effectively acting as a namespace for a collection of functions. So a bit of time in the playground resulted in this experiment:

proc fns {
  return [
    upper: (proc { |x| strs:to-upper $x })
    lower: (proc { |x| strs:to-lower $x })
  ]
}

(fns).upper "Hello"
--> HELLO

(fns).lower "Hello"
--> hello

A good first start. This is theoretically possible in the language that exists at the moment. It’s not perfect thought. For one thing, the call to fns needs to be enclosed in parenthesis in order to invoke it. Leaving them out results in an error:

fns.upper "Hello"
--> error: command is not invokable

The same is true when using variables instead of procs. I tried this experiment again using variables and it came to the same limitations:

set fns [
  upper: (proc { |x| strs:to-upper $x })
  lower: (proc { |x| strs:to-lower $x })
]

($fns).upper "Hello"
--> HELLO

$fns.upper "Hello"
--> error: command is not invokable

Obviously the parser needs to be changed to add additional support for the : operator, but I also need to fix how strong . binds to values too. But I think this may have legs.

My latest YouTube binge has been Drew Gooden, and his videos on YouTube and Instagram influencers and trends: an area of online culture I know nothing about. I’m kinda glad that I stayed well away from that area of the internet. Seems like more drama than I can handle. πŸ“Ί

Getting a new cooler installed tomorrow. The one I have, which I think is as old as the house, is on it’s last legs. It struggles to cool the house, only managing to keep the inside temperature steady if I turn it on early enough, and leaks profusely. I shutter to think what my next water bill will be.

The new unit’s going to be another evaporative cooler. Everyone I talk to tells me I’m crazy, and I should just go with reverse cycle. And I will agree that it’s a bit of a gamble. But it’s worth trying, given the quote I was given and the fact that I don’t have to do any major modifications to the house. If it doesn’t pan out, there’s nothing stopping me from getting reverse cycle later.

I did pick the worse time to do it though: we’re going through a heatwave that won’t break until Tuesday. At least it’ll give me an opportunity to do a fair before/after comparison.