Keith's Notes

Thoughts on Clojure (after a week)

So last week I started learning about Clojure. I'm starting to get a taste for it and thought I'd share some very early reactions.

First, it's DIFFERENT. The syntax is just really different. Pretty much everything is enclosed in parentheses. All functions are, and everything is a function, so... yeah.

In most languages, you'd do something like this:

println(value1, value2)

In Clojure, it's

(println value1 value2)

If you needed to compute one of the values that you pass into the function, you'd possibly create a variable for that.

int value1 = 2 + 3
int value2 = 5 + 6
println(value1, value2)

In Clojure, it seems like its preferable to inline this kind of thing. Also, what you'd think of as operators are also actually functions, so do add two numbers, it's

(+ 2 3)

So the above becomes

(println (+ 2 3) (+ 5 6))

You could create interim variables usind def:

(def value1 (+ 2 3))
(def value2 (+ 5 6))
(println value1 value2)

But the compiler does give you a warning for this inline def so I think it's frowned upon.

There's another way to create interim variables using the let function, which I'll describe below. But it's a bit more complex.

Even function definitions are actually functions, so in another language, you might have

func addTwoNumbers(a, b) {
  return a + b
}

Now you'd do:

(defn add-two-numbers
  [a b]
  (+ a b))

Silly example, but you get the idea. But even that example raises a few more points.

  1. Most languages go for camelCase or snake_case. Clojure is one of the few I've seen that favors kebab-case for identifiers. My first reaction was that it would break everything because it would try to subract case from kebab. But subtraction would be done with a function call (- something something-else) so there is no conflict.

  2. The variables in square brackets represent the arguments to the function. Didn't see that coming.

  3. Clojure style seems to like stacking up the matching parentheses on the end of the last line of a block. I would have done this like

(defn add-two-numbers
  [a b]
  (+ a b)
)

But that doesn't seem to be the preference. Here's the last line of a function I wrote recently.

...
               result)))))

I'd be dead if my editor didn't highlight matching braces.

  1. Where my commas at? Apparently they are optional, but none of the code I'm seeing is taking that option.

Of course most of these are stylistic choices, but when in Rome...

But let's go back to the brackets. They can be used in several different ways. The simplest is just a vector of values:

[1 2 3 4]

The other one I just showed is to get the parameters of a function.

The most confusing one is in binding values to identifiers. So far I'm mostly seeing this in let statements.

(let [a "foo" b [] c 7]
  (println a b c))

This one was the toughest for me to read at first. I was like, "OK, we have vector of six values..." but that doesn't make any sense. In fact, the let function creates a closure, lets you create some local variables bound to other values, and use them within that closure. So this is really saying:

Some code lists the bindings out in a single line like that. More readable code uses some line breaks in lieu of commas:

(let [a "foo"
      b []
      c 7]
  (println a b c))

You can also do destructuring with square brackets. One place you'd see this is in function parameters. Say you have a function that gets passed a collection of some kind. You might want to do something with the first two elements of that collection. You could do:

(defn foo
  [[a b]]
  (println "the first two elements are" a b))

Here, the outer brackets signify the function param(s) and the inner ones do the destructuring.

Or you might want to get the first element and do something with the rest of the elements:

(defn foo
  [[a & everything-else]]
  (println "the first element is" a)
  (println "all the other stuff is" everything-else))

Powerful, but it takes some practice to be able to actually think with it all.

Anyway...

OK, so that's all just a rant about the differences. But it's fine. I chose Clojure because I knew it would be very different and I'd have to change my thinking about how to code things. It's certainly having that effect, which is great.

I understand that Clojure is a Lisp, i.e. one of the languages based on Lisp. So a whole lot of these differences are probably very similar in any of those languages.

Finally, one thing that really stuck out in my mind was how easy a language like this must be for an interpreter or compiler to parse. In examples like this, you can pretty much see the abstract syntax tree right there in the source code:

(println (+ 2 3) (+ 5 6))

Anyway, that's just some early thoughts. I'm continuing to bang away and will probably have more ramblings later.

Clojure: It Begins

Last Friday I said to myself, "Self," I said, "you should learn a new programming language."

First try: Yaksha

I had run across a new project called Yaksha which looked pretty interesting. And it has a built-in graphics api, which was appealing. So I downloaded it and went to do a simple hello world program and that's where I saw its limitations. It's a brand new project created by one person. The language documentation is great, but there are almost no resources out there telling you how to get started. I'm sure I could have figured it out, but I didn't really want to dive into something so raw.

Second try: nim

So I moved on to another language that I've heard word of occasionally, nim. This looked pretty interesting and I was able to get a simple program running very quickly. This was just a bit later on Friday night. But one thing that's going to be pretty important to me if I'm going to use a language long term is tooling. I use neovim as an editor and it's super configurable. I've been using neovim's built-in language server protocol (LSP) to do things like code completion and real time error checking. So I wanted to get that working with nim. I banged away at this for the rest of Friday night and off and on throughout the day on Saturday. There is an LSP module for nim, nimlsp. It is supported by the LSP library I'm using in neovim. I got it to connect to the file I was editing and recognize it, but I could not get it to actually do anything, like error checking or code completion. I've set up LSPs on other languages just fine and I pretty much know how they go, but for the life of me I could not get the nim one to work. So it was time to move on.

Third try: Clojure

Clojure was next. I've never worked with a Lisp type programming language and really haven't done too much with functional programming. Clojure looks horrible to read at first glance. It just seems all weird to me. But, there's a whole world out there that I know nothing about, so I figured why not dive into it a bit?

I was able to get all the tooling set up to be able to write and run a program very quickly. Setting up an LSP was a little confusing, but it had more to do with project structure than the LSP itself. Honestly, it might have been the same basic problem with nim, but for whatever reason I solved it pretty quickly with Clojure.

I announced on Mastodon that I was starting to dabble in the language and got a recommendation for a book:

mastodon book recommendation for Clojure for the Brave and True

https://triangletoot.party/@shovemedia/110171185120884296

It's "Clojure for the Brave and True" by Daniel Higgenbotham. You can buy it or read for free here: https://www.braveclojure.com/clojure-for-the-brave-and-true/. I have an O'Reilly subscription so I'm reading it from there.

I got through chapter 2 so far. OK, actually I got through chapter 1 and skipped chapter 2 which is all about emacs, which I'm not going to use. But I skimmed it to be sure there was nothing useful for me in there.

So, I've begun my journey and I'm kind of excited. I see there are Clojure bindings for Cairographics, so that should keep me engaged for a while. If this goes well, I'll probably port my bitlib project to Clojure. But that's getting a bit ahead of things at this point.

Shower Thoughts

Confession: I've had a confusion for a long time. And I think that it makes me sound stupid.

It's got to do with that old question, "why does a mirror swap left and right, but not up and down?"

I understand that the question doesn't really make sense. I know that it's not something weird about mirrors, just something weird about our brains and perception. But if asked to explain why we see it this way, I would probably just mumble something about symmetry. This morning, it was stuck in my brain for some reason. This post is what my mind went through as I overly dwelled upon this. Mind you, I could probably find a decent article that explains this a lot better, but I thought that the gyrations my mind went through were pretty interesting, at least to me.

So you have a right hand. Viewed from your back, that hand is oriented in a clockwise position from your head. And your left hand is over there in a counter-clockwise location. When you look at another person head on, things are reversed. Their right hand is counter-clockwise from their head and their left hand is clockwise. But when you look in a mirror and move your right hand around, the hand that moves in the mirror is clockwise from that person's head. And what you know is your left hand is counter-clockwise.

The question of why doesn't the mirror swap up and down makes no sense because it's not going to put your head on the bottom and your feet in the air. OK, some mirrors might do that, but we're talking about an ordinary flat mirror. Because we have left-right symmetry, we can see that left and right get swapped from how we usually see people, but not up and down.

So I figured there must be some thought experiment I could do that would cause the same issue on the other axis. What if we had two heads? One that faced downwards and one upwards. And our arms just stuck out of the middle of our bodies and we kind of floated in the air. And we had an even number of fingers so that our hands were up-down symmetrical?

It still wouldn't work. You'd look at yourself and say, "my up head is still up and my down head is still down, but my left and right hands are swapped. So it's not just symmetry.

I started thinking that it's got to do with the fact that we have an absolute orientation of up and down. Up is always towards the sky and down is always towards the ground. At least experientially. But left and right are always relative. You point to the right and it's one direction, but if you turn around, then right is in the other direction.

Another thought experiment I did:

First, imagine lying down on some kind of platform facing a mirror. You'd still see your right and left hands swapped, but they would be swapped vertically this time! But you wouldn't think your head and feet were swapped, even though they extend to the left and right. We fixed the mirror! Or at least changed how it was seemingly not working right.

Now, say we were the same as we are now, but we floated along on our sides like fish. Say the way we floated was always such that what we call our right hand would always be up in the air, and our left hand would be down toward the ground. We'd probably call our left hand our "down hand" and our right hand our "up hand". Then, whenever we faced another person, their feet would be near our heads and vice versa.

But if we floated by a mirror, it would be a very different experience. The person we saw in the mirror would be all wrong. Their down hand would still be down and their up hand would still be up. But their head and feet would be flipped. So now we've proven that it's not even about symmetry at all! The symmetrical part of our body didn't get flipped, but the unsymmetrical part did. Amazing.

So this is way more about frame of reference and psychology, which I already knew, but I think I understand it more. But at the same time, it's still confusing.

As I was writing this though, I thought of another way of thinking about it. When you look at another person face to face, it's because they turned around to look at you (considering you were initially facing the same direction). Thus, their right hand is now on our left, and their left hand is now on their right. Since most of us see other people more than we look in mirrors, that's what seems normal. We forget that they rotated 180 degrees on their own y-axis. And the mirror doesn't do this. Because we always rotate on our y-axis, it's only our left and right parts that cause this cognitive dissonance. If we were walking in one direction and wanted to turn around and go back and then flipped upside down and walked on our hands, then I think our mirror experience would be very different. I guess.

Again, there's probably some Youtube video that explains this in a much more logical way. I bet it touches upon some of these same things. But I like banging away on them myself.

Stickers!

Background

About eight years ago (my god, time does fly - my brain wanted to say 2-3 years ago) I got something called a Silhouette Portrait vinyl cutter. You might have heard of the Cricut machines that are somewhat popular among crafters these days. This is essentially the same thing, just a slightly less popular brand.

Silhouette Portrait vinyl cutter

This image is the more up-to-date Portrait 3, but it looks pretty much the same. It's the size of a small printer. That unit inside holds the cutter and slides left and right on a rail. You feed the vinyl in underneath it and the rollers move it in and out. So it cuts in two dimensions, but the cutter moves on one axis and the vinyl moves on the other axis.

Of course, you can put things other than vinyl in there. Paper, cardstock, etc. and make all kinds of things. There are even other attachments, such as pen holders, so you could use this as a plotter. I've only made a few tentative tests using it like that though. None were super promising, but with enough trial and error, I bet I could get something decent out of it. Though probably not comparable to an Axi-Draw.

The Portrait can take material up to about nine inches wide. But you can use a roll of vinyl, so if you wanted to make a huge, several-foot-long banner or something, you could. I've been eyeing the Silhouette Cameo, which is the newer model that can go twelve inches wide and has all kinds of other bells and whistles. There's even a souped up twenty-four inch wide version.

The reason I first got this was back when I was making knives. I wanted to electro-etch a logo in the metal. I built my own electro-etcher machine and it works great. I cut out a sticker to serve as a mask - where the logo was the open spot and the rest of the sticker masked the metal. I haven't used it for that purpose in many years though.

When I started my current job, I made various stickers for my coworkers, some of them using the faces of said coworkers. This was fun.

Vinyl sticker of a coworker's face

Current

Though I haven't even made any of these stickers in a few years, one of my coworkers asked about them, so I whipped up the batch you see above. This sparked a bit of a creative streak in me and I've been designing and making stickers for the past few weeks. Here's a bit of a gallery.

Vinyl sticker of the Led Zeppelin logo Vinyl sticker of the Rolling Stones logo

Just realized I need to weed out a piece of the "n" in "Rolling"!

Vinyl sticker of Felix the Cat

Felix is my favorite so far! It came out so good!

Vinyl sticker of the Fireflies logo from The Last of Us

Fireflies logo from The Last of Us. This one has a bit of a design flaw - those sharp corners start to peel really easily. I'm surprised how well this has held up being on the back of my phone, going in and out of my pocket so often. But its days are numbered.

Vinyl sticker of a cartoony skull Vinyl sticker of another cartoony skull

I found this skull design on a royalty free site and altered it to make four different skull shapes.

Vinyl sticker of a peace sign

The Process

You need a vector graphic to be able to cut a sticker. I usually start with Inkscape on my Linux laptop. If I can find a nice SVG to start with, that's great. But as long as I can get a decently high-res, clean, ideally black and white image, Inkscape's "trace bitmap" function does a fantastic job at turning it into an SVG path. Sometimes I might tweak it a bit as needed, and then save it as a DXF file. Why DXF, you ask. Hold that thought.

Then I open up the software that Silhouette puts out - Silhouette Studio - on my Mac. It's ... OK. There's actually a plugin for Inkscape that will send drawings to a Silhouette machine. I got it to work with decent quality, but it's pretty clunky and takes a lot of fiddling. The official app isn't great, but it has lots of built in presets and useful functionality. I'm using the free version of the software, which is pretty limited. Like, it doesn't even import SVG files. That's why I saved the drawing as DXF from Inkscape. It does open those. Maybe eventually I'll pay the extra $25 for the next step up in features, but since I like Inkscape as a starting point anyway, that's fine.

I size and position the graphic as needed. Usually once I do a successful test run, I'll tile the shape and cut a bunch of stickers at once. If I'm using a roll of vinyl or a full sized sheet, I can just insert it directly. But if I'm using up some smaller scraps, there's a plastic cutting mat you can use. Its surface is a bit sticky, so you can put the vinyl (or whatever) on the mat and feed the mat in.

You have to set the depth of the cut so that it just goes through the layer of adhesive vinyl, and not through the backing paper. The software has presets for this though, so it's pretty simple. I have found that I have to make my cuts one notch deeper though, as it wasn't going fully through all the time. Might be time for a new blade.

Once the piece is cut, you have to peel off all the parts that aren't part of the final sticker. This is known as "weeding". Then you cut a piece of transfer paper that's just bigger than the sticker. The transfer paper itself is a bit sticky. You peel the backing off the transfer paper and stick it onto the front of the sticker and smooth it down and trim it to size if you want. Now your sticker is ready for application, or distribution.

When you're ready to apply the sticker, you peel the backing paper off the original vinyl sticker, leaving it stuck to the transfer paper. Then you put the sticker on a surface, smooth it down and peel off the transfer paper. Done.

The Future

There is no real future. Just having fun. I'll keep making these and keep sticking them to various devices and surfaces around the house, and I'll give them away to friends, coworkers, family who want them. I'm not going to try selling stuff that is pretty often copyrighted and someone else's creation.

But if someone has an original design they'd like to make stickers out of, hit me up and maybe we can work something out. That'd be fun.

Updates and Accessibility

Been posting some other stuff but making continuous improvements to this site. Lots of new styling and I think it's looking pretty good, while still being very minimal, which is what I want. I put some work in on making things mobile friendly and from what I can see that's looking fine. Added a menu where you can see the most recent five posts or an index of all posts.

I think one of the next things I want to work on is accessibility. We've had a big push on it at work, with training and all, so it's forefront in my mind. I just need to put in the time to figure out how to do that within 11ty.

Update

I just ran the site through Lighthouse and Wave, two of the top a11y test tools. Fixed a couple of small items and now looking damn good!

Lighthouse

Lighthouse results: 100% performance, 100% accessibility, 100% best practices

Wave

Wave results: Congratulations! No errors were detected.