Friday, April 17, 2020

This is lame

<rant>
I'm reading in a file called “telemetry.json”. The contents resemble this:
start_time:2020-04-17:08-32-45
end_time:2020-04-17:09-18-07
  • Despite the file name, this isn't JSON format.
  • The timestamps are in some weird format. Who puts dashes between the time entries?
  • It's anybody's guess whether this is local time or GMT.
So instead of just writing objectMapper.readValue(), I had to write a page and a half of code that reads the file a line at a time, splits the line into a key and value string, runs the value string through a custom date parser, and constructs a telemetry POJO to hold the results. I'm guessing this is in local time. I just have to hope the machine that wrote the file is physically in the same time zone as the machine that ends up reading the file (that'll make a truly obscure bug).

There are libraries to write JSON files. Someone was too lazy or ignorant to use them. There is a standard date/time format, ISO 8601, and libraries to read and write timestamps with actual time zone information. Someone was too lazy to use them. Anyone want to take a bet that this is written with a Python script? So now I have to do the extra work to make up for someone else's laziness.

Amateurs.

I lose the bet. It's 13 different shell scripts.
</rant>

Tuesday, February 4, 2020

Apropos of nothing

I am visiting San Jose, California to meet with my remote boss and the team I am working on. I was sitting at the hotel bar last night when an older gentleman with a large bushy beard came in, walked up to the bar, plunked down a coupon, and ordered a scotch, Macallan 12-year old, neat. I've never seen someone get a scotch with a coupon before, so I remarked upon it. The gentleman sat down and we started chatting. The bartender needed approval from her boss, which she got, and then she poured half a shot before the bottle ran out. She asked if another half shot of Macallan 10-year old was an acceptable substitute and got a reluctant yes.

I eventually got around to asking what the gentleman did for a living (I understand that in some countries asking about someone's work is considered rude, but not here). To my surprise, it turned out he is working for the same company I am. He was visiting from North Carolina where he worked out of the Durham office. He was one of the early employees of the company and knows a lot about the architecture of the product and was out here to train new sales engineers about how the product worked. Although he was in sales, he was an engineer, so we had a lot in common. We talked late into the evening about work, college, cars, music, mathematics, and other various topics. I got a lot of good background information on the company and the product from him.

I suppose there's no real point to this story. It's just interesting how chance meetings sometimes turn out.

Saturday, January 25, 2020

User Interface

I don't care for UI code.  I don't care where the pixels end up or how round a button is.

Case in point, I just added a column to a table.   The problem:  the column isn't wide enough to hold the data it displays, so the data runs out of the column and into the next one, overwriting the text there.  Simple problem.  Simple solution?  No.  It was easy enough to add a style element to the column making it wider.  But now the column header didn't line up with the column.  I added the same style element to the header, but it was completely ignored.  In fact, nothing I could think of would make the column header simply be wider than it was.  There were 12 columns, and each header took 1/12th of the space.  That was written in stone.

Well, almost.  I made there be 13 columns, and set that one particular header to span 2 columns.  I had to do the same to the data and make it span 2 columns as well.  It does not look good, but that particular column is now wide enough to hold the data, and the header lines up above the data like it should.  Unfortunately, each of the other columns are just that much narrower to spoil the layout.  There are some columns that would naturally be nice and narrow and give me lots of  extra room to play with, but no, there are now 13 columns, each taking 1/13th of the space, except for one that's twice as wide.

This is why I don't like UI.  Why does the table data pay attention to the "width" directive and the table header ignore it completely?  I don't know.  Will anyone complain that my "solution" is ugly?  You bet.  I even think it is ugly.  I've already wasted enough thought and energy trying to make it prettier, but to no avail.

Tuesday, January 21, 2020

Thanks to my readers

I want to thank everyone who reads what I post. I write blog entries to try to gather my thoughts into coherent forms, and knowing that someone might actually take me seriously keeps me on my toes. I don't want to confuse or mislead anyone.

Even if no one takes me seriously, though, at least they get some cheap entertainment watching me try to muddle through my confusion.

I'm starting a new job today, so blog posts will likely be shorter and less frequent. With luck, however, I've gotten back into the habit of posting regularly.

Please keep the feedback coming. I read every comment, though I don't answer many (most don't require an answer).

Thanks again.

Sunday, January 19, 2020

Group actions

In a previous post I discussed how the elements of a group could be seen as “acting” on other elements directly as if they were functions. There's a more direct way of getting at this, but it is so simple, it might be a bit of a disappointment. But we'll take things a bit further.

First, let's consider the group with the element 0, 1, and 2 where the binary operation is addition modulo 3. It is closed, associative, 0 is the identity element, and 1 and 2 are inverses of each other. It is a group.

;; save the real multiply
> (define real-multiply *)
real-multiply

> (define (* a b) (modulo (+ a b) 3))
*

> (* 0 1)
1

> (* 1 1)
2

> (* 2 2)
1
But the elements of the group aren't functions, they are integers. They cannot directly act on other elements:
> (1 2)
Error -- application of non-procedure object 1

There's a simple trick to get directly at the “action” of an element on another element. Just “curry” the * operator:

> (define (get-action operator element)
    (define (action other-element) (operator element other-element))
    action)
get-action

> (define action-of-1 (get-action * 1))
action-of-1

> (action-of-1 2)
0

> (action-of-1 1)
2
See, that's almost disappointingly simple. Of course the other elements in the group will have their own actions obtained in the same way:
>(define action-of-0 (get-action * 0))
action-of-0

>(action-of-0 1)
1
No surprise that the action of the identity element leaves things unchanged.

Let's consider a different group. This group also has 3 element, '(a b c), '(b c a), and '(c a b). The operator is a funny sort of list rotation: if the first argument is '(a b c), it doesn't rotate the second argument at all. If the first argument is '(b c a), it rotates the second argument left by one position. If the first argument is '(c a b) it rotates the second argument by two positions:

> (define (funny-rotate first second)
     (cond ((equal? first '(a b c)) second)
           ((equal? first '(b c a)) (rotate-left second))
           ((equal? first '(c a b)) (rotate-left (rotate-left second)))))
funny-rotate

> (funny-rotate '(a b c) '(b c a))
(b c a)

> (funny-rotate '(b c a) '(c a b))
(a b c)
You've probably noticed by now that this second group is a disguised version of the first group with
0 <=> (a b c)
1 <=> (b c a)
2 <=> (c a b)
(modulo (+ a b) 3) <=> (funny-rotate first second)
This is an example of an isomorphism between two groups.

Suppose, for the sake of argument, that this second group is just like it currently is, but it had a few more elements that funny-rotate knew how to handle. But otherwise everything else is the same. Then there would be elements that didn't have a one-to-one mapping. This wouldn't be an isomorphism but a monomorphism. These are both kinds of homomorphisms between groups. (The wall of terminology appears.)

Let's back away from the wall of terminology for a moment and go back to the action of an element. Notice that the action of an element is a function. It maps elements to elements. The action is actually a bit more abstract than that. We can swap out the elements with the elements of an isomporphic group. So the action-of-1 which carries 0->1,1->2, and 2->0 can be considered the same action that carries '(a b c)->'(b c a), '(b c a)->'(c a b), and '(c a b)->'(a b c). Even though 1 doesn't even belong to the same group as '(a b c), we can consider action-of-1 and action-of-bca to be the same thing.

The action is even a bit more abstract. We can swap out the operator of the action, too. Here we have to be careful to make sure we don't ruin the group structure when we do that. To give an example, consider the conjugate of some action on some argument: (conjugate (f g)) = (h f) where we've found the h that makes (h f) equal to (f g). Recall that h is equivalent to fgf-1. We can, without ruining the group structure, give each element in the group the action of conjugating it's argument. (Just to throw out a little more terminology, when a group element acts on other elements in the same group, it's called an automorphism.)

We can, if we are careful not to ruin the group structure, swap out both the operator and the elements of the action of an element. But let's not do that. Let's return to simply getting the elements of the group to act on each other by currying the operator. This is a nice, easy to understand automorphism. In our examples, our groups have three elements. You can imagine them as sitting on the corners of a triangle. One element is the identity element, it leaves everything alone. One element bring 0->1, 1->2, and 2->0 or the equivalent, and the final element undoes it, bringing 0->2, 1->0, and 2->1. If they were sitting on the corners of a triangle, one element rotates clockwise, one elements rotates counterclockwise, one element does nothing. These automorphisms simply permute the elements of the group. And it has to be so. Because each element has an inverse, we have to be able to determine what the “input” element was associated with the “output” element, so each unique input must lead to a unique output, which makes it a permutation.

Ok, one more thing. Permutations are associative. If you have a set of permutations that are closed, includes the identity and all the inverses, then you have a group of permutations (called the symmetric group, even though there are many because it depends on how many items you are permuting. Better to parameterize by the number of items and call it symmetricn.) Cayley's theorem says that every group is a subgroup of the symmetricn group acting on the elements. That is, every group has some symmetries associated with it. The symmetries can help you gain insight into the group. This is pretty remarkable: we started with foldable sequences with identity elements and inverse elements, and we ended up with deep connection to symmetries.

I just wanted to get to as far as Cayley's theorem, so I'll end this now. I hope there wasn't too much terminology, but I'm afraid there was enough abstraction to make me dizzy.