simont: A picture of me in 2016 (Default)
simont ([personal profile] simont) wrote2013-07-12 09:41 am

Adages

It's increasingly occurred to me, as I get older and crustier, that I find myself saying things to people that I've said a number of times before.

Some of them are well known adages or observations, of course, but others are things that I wasn't previously aware of, or put together from several other pieces, and they've gradually become a sort of collection of my personal adages and laws and maxims.

A few months ago I idly wondered how many of those I actually had, so I started collecting them in a text file. I've come up with more than I expected, so I thought I'd post them in one go for your entertainment.

Simon's Law of Google has frustrated me since at least 2004: if you google repeatedly for something, fail to find it, and then complain about it loudly and publicly, it will only occur to you after you mouth off that the form of words you used to describe the thing in your rant is the one phrase you hadn't already tried typing into Google, and when you do it will work the first time.

The Law of the Easy Bit is another one I actually bothered giving a name to: if there is an easy bit and a hard bit, you'll devote all your concentration to getting the hard bit right, and embarrassingly mess up the easy bit.

The adage ‘Make simple things simple, make difficult things possible’ is of course well known and not mine; Wikiquote attributes it to Alan Kay. My contribution to it is to find numerous occasions to remind people forcefully that the word in the middle should be AND, not OR. (I'm sure everyone has their own favourite example of software, or something else, which signally fails one side of it due to thinking too hard about the other.)

The wording of the next one tends to waver a bit, but a general theme I keep coming back to is: make things either identical, or totally different. All sorts of annoyances arise when things are similar enough to confuse with each other but different enough to make it important not to confuse them, both in the linguistic case of similar words and also more directly functional questions. (For example, this same principle is the reason why big-endian is better.)

Treating your programming language as a puzzle game is a sign that the language isn't powerful enough. This tends to raise eyebrows when I say it, given my history of treating C as a puzzle game, so I often append even if the puzzles are quite fun! I've so far resisted the temptation to try my hand at language design in an attempt to do properly what I've so far only done by trickery, but there's always a chance that sooner or later I'll be hubristic enough to try…

I'm not sure if the next one should be considered one adage, or two that happen to be worded the same: Write the hard part first. The reason I'm unsure is that there are two reasons I say it, and I suspect they might be actually distinct rather than just ends of a spectrum. One reason is that if a task in many parts has one part that's so hard it might actually turn out to be impossible, then attempting that one first means that if it does turn out impossible you haven't wasted lots of time on the easy parts which now have no use. (In particular, this is advice I give to people contemplating writing puzzle games for my collection: write the random game generator first, because if you can't get that to work, there's no use for the UI code at all.) The second reason is that in cases where the easy job is a special case of the hard one rather than complementary to it, it's probably easier and less error-prone to construct a solution for the easy case by simplifying code that solves the general case than to go the other way and try to build a general-case solution by adding bells and whistles to the easy version.

I'm tempted to call the next one the ‘Law of Conservation of Astonishment’, alluding to the Principle of Least Astonishment. This applies to systems (software or whatever) which try to cleverly guess from context what you wanted them to do: the less often they guess wrong, the more painful it is when they do both because you were expecting it less and are more thrown off-balance, and because the rarer the error the less the system's designers (or anybody else) will have felt obliged to provide tools for coping with it or even understanding it.

On the subject of conservation laws, another thing I often seem to find myself saying is that there isn't a Law of Conservation of Blame. That is, if I can argue that something isn't my fault, that doesn't automatically make it your fault (supposing, that is, that you were the only other person whose fault it might plausibly be), and conversely, if it is my fault, that doesn't necessarily mean it's not also yours!

One that came up just the other day was a thing I use to stop myself agonising forever over decisions: the harder it is to choose, the less it matters. The idea is, if it's hard to decide which of the available options is best, that's probably because they're pretty close to equally good, which means that if you do choose the wrong one you won't lose much.

And finally, here's one that I have yet to work out the best concise wording for, but I'd like one, because I find I have to say it a lot. It's not ‘OK because we're going to do X’ unless you actually do X is perhaps the best I can think of right at the moment. For instance, someone at work will institute a laborious manual procedure (e.g. every time you do some reasonably common thing you must make sure to update three separate wiki pages, a whiteboard and a Word document) and promise that it's only a temporary inconvenience because ‘soon’ the proper automation will come along and then it won't be necessary to do it by hand any more. Of course it never quite does, and next thing you know the hopelessly time-consuming bureaucratic faff becomes enshrined as ‘we've done it this way for years, it can't be that inconvenient, and anyway each of the things you update has someone depending on it who won't want to change’.

Of course, like most adages, these are figurative and/or approximate and/or unproven and/or not universally applicable. The Law of Conservation of Astonishment claims (without evidence beyond anecdote, of course) a negative correlation between frequency and intensity of astonishment, but in order for it to actually keep the average astonishment per unit time constant the relation would have to be exactly reciprocal, and I've no reason to think it is or even any idea of how you could quantify the question. Sometimes it really is necessary to solve an easy special case of a problem before you have the least idea how to tackle the general case; some hard choices do turn out to include one really bad option that wasn't obvious but you could have spotted it with more thought. And it's not impossible that my instinct to write the hard part first may actually be a contributing factor to the Law of the Easy Bit. As with applying any other principle of this level of vagueness, you generally also need the proverbial ‘wisdom to know the difference’.

[identity profile] cartesiandaemon.livejournal.com 2013-07-12 11:43 am (UTC)(link)
LOL. Yeah, very good list.

It's not ‘OK because we're going to do X’ unless you actually do X

Yeah. It seems like a special case of "Your argument is reasonable, but your premises are blatantly false. But I'm embarrassed to suggest you're that self-deluded" :)

there isn't a Law of Conservation of Blame

Yes, this, very much so. It's a perennial fallacy that blame must be exactly 100%, not 0% or >=200% or somewhere between.

the less often they guess wrong, the more painful it is when they do

Yeah, in retrospect I've often seen that, but didn't notice until you put it in those terms.

Write the hard part first

Hm. I think it depends how sure you are that the goal is the actual goal. I definitely agree in the case of your puzzle-game contributors "hi, I've done all the easy bits, can you finish off the impossible 10% for me" isn't helpful.

But there's many other cases where the hardest bit is impossible, but one of the other bits turns out to be amazingly useful anyway, and you don't achieve what you set out to do, but you succeed at something else.

Treating your programming language as a puzzle game is a sign that the language isn't powerful enough. ... even if the puzzles are quite fun!

I think it definitely needs the disclaimer, that it's not a problem with the puzzles, just that if you _always_ have to do that, it would be nice if the language took care of it for you.

I'm curious what your ideal language would be. I'm currently thinking I'd like something like Python but with optional static checking that can turn it into C when you need. But I'm not sure if that's sensible :) And I want people to still need C or C++ programmers so I still have a career, though I obviously can't base my predictions on what would be convenient for me :)

Simon's Law of Google

Hm. This seems like a hint that asking people "how would you google for X" is a necessary step before giving up?

[identity profile] cartesiandaemon.livejournal.com 2013-07-12 03:02 pm (UTC)(link)
Yeah, that seems like a good list :)

I was about to say, it's possibly surprising more languages aren't designed to compile to C, and then let the C compiler writers do all the heavy lifting of making them fast on every platform. But I guess I've just reinvented Java, oops :)

hybrid eg. GC with an optional explicit free operation which causes an allocated thing to become instantly invalid

Yeah, except I always think the of reverse: it seems most variables go out of scope at fairly clearly defined places, and I've grown very fond of having RAII work the C++ way (always) rather than the C# way (every time you use a IDisposable class, you can be leak-safe and exception-safe, provided you remember to add the "don't leak this" boilerplate). But it would be nice to have a garbage collector for things left over. Although I suppose the GC needs to be aware of any other variables which might still hold a pointer to them.
andrewducker: (Default)

[personal profile] andrewducker 2013-07-12 04:25 pm (UTC)(link)
C# + bolt-ons seems to cover a lot of this. Although bits of it will definitely end up puzzle-like :->

[identity profile] strawberryfrog.livejournal.com 2013-07-12 04:32 pm (UTC)(link)
"My biggest blue-sky wish is that I'd like a built-in language feature to construct pieces of code on the fly"

Th next version of .Net should have something like this - http://en.wikipedia.org/wiki/Microsoft_Roslyn

[identity profile] strawberryfrog.livejournal.com 2013-07-15 04:21 pm (UTC)(link)
If you're going to parse it at compile time, then any language with first-class functions will do something much simpler than this, unless I'm missing something. in C#:

Func<int, int> doubler = x => x * 2;

in Javascript:

var doubler = function(x) { return x * 2 };

I know, there's no "compile time" in JS. But it's equivalent syntax anyway.

If it's deferred until runtime, then the c# syntax is far more complex and unwieldy but probably more flexible: http://blogs.msdn.com/b/csharpfaq/archive/2009/09/14/generating-dynamic-methods-with-expression-trees-in-visual-studio-2010.aspx
Edited 2013-07-15 16:23 (UTC)

[identity profile] strawberryfrog.livejournal.com 2013-07-15 04:45 pm (UTC)(link)
I see - you could vary the number of statements with the "generating dynamic methods with expression trees" method above, but it would be a) checked at runtime not compile-time. and b) fugly. Roslyn may address the second issue somewhat, but probably not the first.

Are you sure you don't want to learn Common Lisp?

[identity profile] mdw [distorted.org.uk] (from livejournal.com) 2013-07-15 03:49 pm (UTC)(link)
Let's see...

  • Multi-paradigm: oh, yes. We got that.

  • Fast compilers: check. More or less. Common Lisp is dynamically typed, but with optional type annotations. Good compilers can do extensive type inference. By default, Lisp is fairly safe (array bounds are checked, for example), but this can be turned off locally. Common Lisp was always intended to be compiled, and designed by people who'd already made Lisp implementations competitive with the Fortran compilers of the day.

  • OS bindings: hmm. Every decent implementation has an FFI. The last time I looked, the CFFI-POSIX project wasn't going anywhere, though.

  • Semantics: yep. Pretty good ANSI standard; a draft of it is available online as an excellently hyperlinked document -- the `HyperSpec' -- which, to the tiny extent that it differs from the official standard, probably reflects real life better. Common Lisp nails down a left-to-right order of evaluation, which eliminates a lot of C annoyance; aliasing just works correctly; and while runtime type-checking isn't mandated, all implementations I know of will do it unless you wind the `safety' knob down.

  • Compile-time checking: hmm. A decent implementation will surprise you with how much it checks, even in the absence of explicit annotations. Unlike Python, Lisp implementations will want you to declare variables explicitly or they'll give you lots of warnings. SBCL's compiler diagnostics are less than excellent. The usual clue is that it emits a warning explaining how it elided the entire body of your function, and then you notice that it proved that you'd passed an integer to `cdr' somewhere near the beginning. If you like writing type declarations then you can do that and get better diagnostics.

  • Metaprogramming: Lisp's most distinctive feature is that it's its own metalanguage.

  • Explicit free: no, sorry. The FFI will let you allocate and free stuff manually, but it's a separate world filled with danger.

  • Slabs of bytes: a standard part of the FFI. Don't expect this to be enjoyable, though.

  • AOP: you can probably make one out of macros. Maybe you'll need a code walker.

  • Integer arithmetic: Lisp integers always have the semantics of true mathematical integers. Lisp systems typically have lots of different integer representations (heap-allocated bignums; immediate fixnums which have type-tag bits and live in the descriptor space; and various sizes of unboxed integer used for intermediate results, and as array or structure elements) and use whichever is appropriate in any given case (so `narrowing' occurs automatically, but only when it's safe). A good compiler, e.g., SBCL, will do hairy interval arithmetic in its type system in order to work out which arithmetic operations might overflow. If you wind up SBCL's `speed' knob you get notes about where the compiler couldn't prove that overflow was impossible. SBCL also has some nonportable features for declaring variables which should have wrap-on-overflow semantics instead.

  • Runtime compiler: got that too. It compiles Lisp code to native machine code. And it's used to program-generated code, because of all the macro expansions.

  • (asdf:operate 'asdf:load-op "CL-PONY"). (Not really.)


[identity profile] cartesiandaemon.livejournal.com 2013-07-16 10:28 am (UTC)(link)
Hm. Come to think of it, maybe "deciding on a subset of lisp and a consistent easy-to-use syntax for it" is what language design should be :)