Thursday, December 30, 2021

Scrolling two files curmudgeonry

It's useful to be able to synchronize scrolling through two files, especially for comparing two files that have too many intraline differences.

Vim supports this, with this annoying incantation:

vim -O oneFile anotherFile -c "windo set scrollbind" -c "windo set scrollopt+=hor" -c "windo set nowrap" -c "windo set cursorbind"

Switch between the windows with: ctrl-w ctrl-w

Nowrap is useful because otherwise different sized lines don't display nicely.

Other annoying things: 

  • The documentation can't decide what this feature is called, scroll binding or scrolling synchronously.
  • Gvim doesn't seem to support cursorbind.
Though maybe vimdiff is actually better for most similar use cases. It gets the defaults right, and with gvim it seems to get cursorbind right too.

Tuesday, October 19, 2021

Toki Pona

Toki Pona is a supremely simple constructed language. It's a real Newspeak (from Orwell's 1984), but for good and not evil.

It's very cool. Unfortunately nobody seems to seriously use it for international collaboration. (Apparently this is called auxlang, and obviously English right now is it.)

I was initially grumpy about the "pre-verb" part of speech, but now I believe it's consistent with the general rule that a word modifies its preceding word. For example, a noun precedes its adjectives, and a verb precedes its adverb. So: mi ken pali ==  I can work. mi wile e ken pali == I want the ability to work.

Wednesday, July 21, 2021

Nullaway @Initializer

Nullaway is the currently most practical solution to the billion dollar mistake.

Its @Initializer annotation is one of its practical features; if an object is not expected to be used until one of its methods in executed, that method can be annotated. Then fields can still be statically guaranteed to be non-null if they are initialized in that method, even if they are not initialized earlier.

Nullaway does not, however, statically guarantee that @Initializer methods are actually executed. Therefore, it's better to initialize non-nullable fields in a constructor.

Friday, June 04, 2021

Poor Man's Projectional Editing

Projectional Editing is programming that goes beyond simple text entry. Notable projects are Lamdu and Jetbrain's MPS and to some extent Intellij itself.

Historically, programmers have been reluctant to stray too far from textual programming. A good compromise approach is to represent language features both textually and graphically. For example, the exponents in LegibleMathematics are both superscripted and ^ prefixed.

We could support the property of always keeping the program valid, and still give the feel of textual editing:

  1. as the user types, insert code to make the program valid
    e.g. if the user types if, then we'll insert (true) {}

  2. in the inserted code, automatically select the next bit that isn't forced, to clarify that it will be replaced as the user types
    e.g. in the case of inserted (true) {}, we'll select the (true)

  3. in general, whenever the user types the same thing as the next required bit, just advance the insertion point over it
    e.g. in some existing code if (x) { y(); } the user clicks before the semicolon and types ; }, we won't change anything at all, and just advance the insertion point to after the }

    If we want to support whitespace, then we could change the traversed text to use the new whitespace that the user types.
The above behavior enables the user to just obliviously type brand new code and have it work like in a traditional editor.

The third point is inspired by Parinfer, and Parinfer also has an idea for editing existing code. Inserting a brace somewhere automatically removes the following brace, and removing a brace will insert one as late as possible. e.g.
void f() {
    if (foo) {
        bar();
        baz();
    }
    qux();
    quux();
}

Removing the } after baz() will automatically insert one after quux().

Inserting } after bar() will automatically remove the other } in the function.

Of course we want tab completion, and once we have that and the above, we don't have to worry about intermediate invalid edits. In languages with stronger types than java, it could be that there isn't necessarily any automatically choosable value, in which case we'd have to use typed holes. In mainstream languages, we could use null at the worst.

It's nice to default to a value that causes the new code to essentially be an identity function, e.g. if the user types + then we'll append 0. If the user types * then we'll append 1. But of course this isn't always possible.

Changing a symbol usage changes just that one usage. Changing a symbol at its definition (or initialization in python for example), instead refactors the symbol and renames all of its usages. Changing a signature to remove parameters just updates all usages to not supply those parameters. Changing a signature to add parameters updates all usages to supply a default value as above.

Lastly, with this approach we can be very clever about cut & paste. A "cut" which would involve an invalid program is no trouble; when the "paste" comes, we can do the appropriate refactoring. In the example function above, if the user cuts the baz() call, and pastes it outside the function, we can automatically change the code to:
void f() {
    if (foo) {
        bar();
        g();
    }
    qux();
    quux();
}

void g() {
    baz();
}

And of course select the new function name g so that it can be easily changed. And some pastes would be illegal, e.g. pasting qux(); quux() in place of foo.

So, this "always valid" approach is very nice for refactoring. Its main benefit however is to enable powerful live coding, e.g. devcards.

Git gotcha

Since the rise of github, most teams build software using a pull request workflow. Therefore the local "master" branch is a bit of a gotcha. You can commit to it, but that will only ever cause you grief. That is, you'll forget about those commits, and they'll eventually cause a conflict, and you'll get something mysteriously broken when you just want "master". Fix it with this:

       
            git branch -D master
            git branch master origin/master
       
 
And actually consider not having a local master at all, and always using origin/master instead.

Wednesday, March 03, 2021

Watches making sure you're "up"

Smart watch alarms used for waking up in the morning should have a feature: sound until standup!

That is, you can configure a alarm to continue playing or vibrating until the watch detects that you are standing up (using barometer, accelerometer, gyroscope or whatever). Obviously all other easy ways to turn off the alarm would have to be disabled.