Compare commits

...

5 Commits

Author SHA1 Message Date
Smaug123
8b2bab9baa Add WoofWare.Myriad post 2023-12-31 13:48:53 +00:00
Patrick Stevens
c211ad7df3 Add iOS interface post (#6) 2023-12-31 13:34:08 +00:00
Smaug123
fe90bc0405 Update dep 2023-12-28 21:25:35 +00:00
Smaug123
b35c219d0e Bump flake 2023-12-02 11:01:36 +00:00
Patrick Stevens
5076d02c81 Add post about stacked PRs (#5) 2023-10-18 21:10:33 +01:00
4 changed files with 123 additions and 7 deletions

14
flake.lock generated
View File

@@ -80,11 +80,11 @@
"systems": "systems_2"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
@@ -104,11 +104,11 @@
"scripts": "scripts_2"
},
"locked": {
"lastModified": 1696175612,
"narHash": "sha256-8V8klzc7T3EdAdS4r8RRjNvTTytQOsvfi7DfK6NFK6M=",
"lastModified": 1703798553,
"narHash": "sha256-dDym75Eq6TIw9IrokBWwSoto0/l3nxFGpH4/VZkeqrQ=",
"ref": "refs/heads/main",
"rev": "ac0b0180304bce7683dc8b4466a6e92b339c0b7e",
"revCount": 15,
"rev": "d441f6c507ecbfc7e95440ca8f021fcec6cb4767",
"revCount": 18,
"type": "git",
"url": "file:/Users/patrick/Desktop/website/static-site-images"
},

View File

@@ -0,0 +1,53 @@
---
lastmod: "2023-10-18T20:36:00.0000000+01:00"
author: patrick
categories:
- programming
date: "2023-10-18T20:36:00.0000000+01:00"
title: Squashed stacked PRs workflow
summary: "How to handle stacked pull requests in a repository which requires squashing all history on merge."
---
Recall that the "stacked PRs" Git workflow deals with a set of changes (say `C` depends on `B`, which itself depends on `A`), each dependent on the last, and all going into some base branch which I'll call `main` for the sake of this note.
The workflow represents this set of changes as a collection of pull requests: `A` into `main`, `B` into `A`, and `C` into `B`.
# Problem statement
The stacked PRs workflow is fine as long as we *merge* each pull request into its target, because then Git's standard merge algorithms are "sufficiently associative" that the sequence of merges tends to do the right thing.
(Of course, Git's standard merge algorithms are not associative; see [the Pijul manual](https://pijul.org/manual/why_pijul.html) for concrete examples and discussion of why this is inherently true.)
But if we *squash* each pull request into its target, then the only way we can merge the entire stack is to merge `C` into `B`, then `B+C` into `A`, then `A+B+C` into `main`.
Any other order, and the rewrite of history in the squash causes the computed merge base of our source and target to be very different from what we actually know it is, and this almost always causes the merge to become wildly conflicted.
For example, if we squash-merge `A` into `main` (which for the sake of argument should be a fast-forward merge, except that we've squashed), then we construct a new commit `squash(A)` whose tree is the same as `A` and which has the parent `main`; then we set `main` to point to `squash(A)`.
The merge base of `B` with `squash(A)` would be simply `A` if we hadn't squashed, but `A` is no longer in the history of `squash(A)`, so the merge base is actually `main^` (i.e. `main` as it was before the squash-merge); and the merge of `squash(A)` and `B` with a base of `main^` is liable to be gruesome.
So we can't cleanly merge `B` into `main = squash(A)`.
The clean problem statement, then, is:
> How do I squash-merge the stack in the order `A -> main`, `B -> main + A`, `C -> main + A + B`, without having to resolve conflicts at each step?
# Solution
Since we're squashing into `main` anyway, we should feel free to make a complete mess of history on our branches.
* Squash-merge `A` into `main`.
* Merge into `B` the `A` commit that's immediately before the squash into `main`. (This should be clean unless you made changes to `A` which genuinely conflicted with `B`, so this is work you should really have done in preparation for the review of `B` anyway.)
* Fetch `origin/main` locally, and merge into `B` the `main` commit that's immediately before the squash of `A`. (This should be clean if you've been hygienically keeping your branches up to date with `main` by merging `main -> A`, `A -> B`, `B -> C`. If it's not clean, again this is work that you would have to do anyway even in a non-squashing world.)
Now `B` is up to date both with `main` and `A` as of immediately before `A` was squashed into `main`, so it should be the case that merging `main + A` into `B` would be a no-op: it should not change the tree of `B`.
However, we aren't merging `main + A`.
We're instead merging the squashed `main + squash(A)` for some single commit `squash(A)` which Git thinks is completely unrelated to `A`, but which in fact has the same *tree* as `A`.
So the last step is:
* Merge the squashed `main + squash(A)` commit into `B` with the `ours` strategy: `git merge $COMMIT_HASH --strategy=ours`. That is: since we know `B`'s got the right *tree*, but its history is woefully incompatible with `main + squash(A)`'s history, we just do a dummy no-op merge to make their histories compatible again.
(Then merge this back up the stack, by merging the new `B` into `C`.)
## The state after performing this procedure
* `A` has been squashed into `main`.
* `B`'s tree is as if `A` were merged into `main` and then the resulting `main + A` were merged into `B`.
* `B`'s history contains the squashed `main + squash(A)`, so subsequent merges of `main` into `B` or `B` into `main` will be clean.
* `B`'s history looks a bit mad, but we shrug and move on.

View File

@@ -0,0 +1,43 @@
---
lastmod: "2023-12-31T12:49:00.0000000+00:00"
author: patrick
categories:
- uncategorized
date: "2023-12-31T12:49:00.0000000+00:00"
title: iOS interface
summary: "A bunch of ways the iOS user interface is bad, and some undiscoverable features."
---
As I think is generally agreed, the iOS user interface is really very bad.
I use iOS because it has fewer fundamental bugs than Android does, but I *much* prefer Android if only it worked.
Here are some of my gripes, and some features it's completely impossible to discover but which are necessary to know.
The list is incomplete: it's just what came to the top of my head over about half an hour.
* Keyboard layout, dead space: As far as I can tell, there is genuinely dead space on the outside of the A and L keys on the QWERTY layout. There is simply no need to demand this much accuracy from the typist.
* Keyboard layout, symbols: every keyboard is nerfed compared to the corresponding Android keyboard. The idiom appears to be to hide symbols behind the "symbols" key, rather than being able to hold any key to reveal more symbols. Microsoft SwiftKey on Android was brilliant: you could access symbols either by clicking the "symbols" key, or by knowing its "hold a key and swipe" shortcut. On iOS, the best you can do is swipe from the "symbols" key, which means your thumb is traversing a lot of the screen a lot more frequently.
* Keyboard layout, general paucity: I literally had to Google how to type one of the characters in one of my passwords.
* Keyboard layout, numbers: it appears to be standard to omit a number row from the keyboard. SwiftKey at least lets you turn this on, but I believe the OS secure keyboard (for password entry) does not have a number row; certainly I have several times been in an interface where I wanted to type numbers but had to go through a menu to do so.
* Filling in forms. The "Done" button is always at the top of the screen, which is at the opposite end of the screen from where you are entering data into the form.
* Filling in forms. The "Done" button appears as flat blue text, with nothing to suggest that it is a button. (This is part of a more general problem that it's generally very unclear what you can interact with.)
* The Home screen is almost completely non-customisable. On Android, you can move icons around however you like, and in particular you can put empty space between icons.
* Calendar. The Calendar app is *weirdly* hard to use. It's miles worse than stock Android's. Why is there no week view that shows you, like, the events happening this week? The best they have is a complete chronological listing of all events in your calendar, which loses any spatial intuition of when events are throughout their days.
* Login. The "OK" button is on the other side of the screen from the numpad: you can comfortably type the numbers with one or both thumbs, but then must stretch or reposition entirely to reach the OK button. (This is a common theme in iOS.)
* Settings search. iOS is *much* worse at finding things in its own Settings app than Android is.
* Search discoverability: a common iOS design idiom appears to be to hide search in a secret "scroll up to view the search box" mode. This is, of course, ridiculous.
* General device search. It's frequently the case that appending to the search term will cause matching results to *vanish* from the list.
* Waking the phone. Latency between "tilt to wake" and the phone actually waking is such that I very frequently press the power button to turn on the phone, then a visible delay occurs, then the phone wakes because I had tilted it, and then the phone sleeps again because I pressed the power button seconds ago.
* The Tips app lets you "practise key gestures" (perfect! every OS should have this!), but… only once at a time? For example, the "Select and edit text" practice (which is absolutely necessary, because of how bad text selection is on iOS) lets you "Try it", but if you pause for too long while you're doing it, it decides you've finished and displays a checkmark, and freezes the interface. If you want to do it again, you have to click "Practise again". Why?!
* The "silence" toggle on the side of the phone has turned into an Action Button on the latest iPhones. Sure, give me customisable buttons, but before that I would strongly prefer a physical toggle like I had on my OnePlus 8 Pro. You can't feel what state your phone is in if that functionality is a button.
* The phone status screen you get by swiping down-left from the top-right is *much* worse than Android's. A bunch of key functionality is hidden behind "press and hold in this area". In a very unusual move for Apple, they have removed almost all the text from this screen, which is the opposite problem from everywhere else in iOS (namely "everything is unstyled text and you can't know it's a button"): everything is clearly a button, but you can't know what it does.
* I have to Google "how to turn off an iPhone". [So does Patrick McKenzie](https://twitter.com/patio11/status/1740623388446769661).
## Necessary features you can't find out about
* You can scroll to arbitrary places in a document. Reveal the scroll bar by scrolling a little, then hold the scroll bar until it blips at you; then you can drag it up and down. (You will need to practise this, because it's extremely finnicky. For example, it appears that once you've quick-scrolled this way once, you cannot do so again until you've slow-scrolled once more.) Why this isn't the default mode I cannot fathom.
* The Calendar is just bad, as I noted above. You can at least see the chronological list of all events in the calendar by double-clicking the "Today" text in the bottom left corner of the screen. This is impossible to discover except by accident.
* You can turn off the phone (without invoking Siri): hold the power button and any volume button simultaneously for a while until it blips at you and gives you an interface you can use. Mnemonic: you can't turn off the phone unless you SHOUT AT IT by turning up the volume while you do it.
## Necessary features you can find out about but which aren't the default
* By default the phone shows notification content on the locked home screen. This is a *really* odd default and is trivially seriously vulnerable to attack. Go Settings -> Notifications, and set "Show Previews" to "When Unlocked".
* You can set a black home-screen background to reduce the home-screen clutter. Go Settings -> Wallpaper, hit "Customise", and then select a "Colour".

View File

@@ -0,0 +1,20 @@
---
lastmod: "2023-12-31T13:42:00.0000000+00:00"
author: patrick
categories:
- uncategorized
date: "2023-12-31T13:42:00.0000000+00:00"
title: Announcing WoofWare.Myriad.Plugins
summary: "Some F# source generators to solve common problems I have."
---
This is a linkpost for [WoofWare.Myriad.Plugins](https://github.com/Smaug123/WoofWare.Myriad), a set of [F#](https://en.wikipedia.org/wiki/F_Sharp_(programming_language)) source generators ([see it on NuGet](https://www.nuget.org/packages/WoofWare.Myriad.Plugins)).
Go and read [the README on GitHub](https://github.com/Smaug123/WoofWare.Myriad/blob/main/README.md) if you're interested.
As of this writing, the following are implemented:
* `[<JsonParse>]` (to stamp out `jsonParse : JsonNode -> 'T` methods)
* `[<RemoveOptions>]` (to strip `option` modifiers from a type)
* `[<HttpClient>]` (to stamp out a [RestEase](https://github.com/canton7/RestEase)-style HTTP client)
* `[<GenerateMock>]` (to stamp out a record type corresponding to an interface, for easy record-update syntax when [mocking](https://en.wikipedia.org/wiki/Mock_object)).
They are particularly intended for `PublishAot` [ahead-of-time compilation](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) contexts, in which reflection is heavily restricted, but also for anyone who doesn't want reflection for whatever reason (e.g. "to obtain the ability to step through code in a debugger", or "for more predictable speed").