Language Warts and Vim Trix
New(ish post)
Last week I published a new post called 10 Most(ly dead) Influential Programming Languages and forgot to share it here. In that time we've gotten a bunch of new subscribers from that post so now they're all seeing the link again. Sorry about that!
(For people who saw the first version: main differences are 1) section on how we determine what "influence" is, 2) checked all the TODOs, and 3) swapped PLANNER for PL/I. One of these days I'll give you my long-awaited rant about Hewitt, I promise.)
Language Warts
So some good news on the Alloy documentation front: we're changing the schedule little bit in terms of how we actually present. I'm going to be unofficially making the first draft of the documentation public, getting feedback on it from y'all, and then revising that to make the official documentation. That way it'll still useful while we're doing edits and polish. Expect to see the first version of the Alloy docs by the end of April.
(Since I have a lot of new readers, I should probably explain what Alloy is. Alloy is a formal specification language, which lets you will write models of software and test the designs themselves for bugs. You can see an example of what this looks like with this introduction, where we prove that a data migration preserves all the properties of the original database. While I do a lot of writing on history and culture in software, formal methods is my first love and specialty, and that's what I do in my day-to-day job. It's really cool stuff!)
I'm trying to make all of the gotchas in using Alloy pretty clear in the docs, and there's a lot of places where I have a big red warning sign saying "do not use this". For example, you should never, ever write one x | one y |
because it will bite you in the butt. Which is interesting to me. If it's so dangerous, why is it possible in the first place?
There's two different kinds of language warts: accidental warts, which were mistakes in the design, and essential warts, which naturally follow from the language design. To give a bit more of a common example: in Python, len
is a function and not a method. This is an accidental wart, and the only reason it still is this way is to preserve backwards compatibility. On the other hand, something like the explicit self is an essential part. If you have a class, you have to write its methods like this:
class Foo:
def bar(self):
...
A lot of people find this annoying, and it also trips a lot of people up. But it naturally follows from the object model, where an object method is defined as passing that object into the class method. Writing baz.bar()
is the same as writing Foo.bar(baz)
. There is no way to remove that self
without making something else worse overall.
The reason this interests me is how it affects how communities discuss these languages. Whenever somebody brings up a wart the community response is usually "that's not actually a problem you just understand the language wrong" or "it's something that sucks that we designed wrong". In other words it's either not a wart or an accidental wart. Communities almost never say "yeah that sucks but the alternatives are all worse" a.k.a. an essential wart. It's a more moderate position and communities have a tendency to become more extreme over time.
Since the formal specification communities are pretty small, I think individuals have a lot of influence to shape how they develop. Which is one reason I'm trying to be very clear about the essential warts in alloy so that there is a culture from the start of accepting and admitting its design trade-offs.
Vim
So a smaller thing. I'm a pretty avid vim user. I also use a ton of macros. Vim makes macros incredibly easy to use, much easier than writing dedicated functions. There's lots of things that could be done in a function that I do with macros instead.
The problem macros is that 1) there is no easy way to annotate what they actually do, and 2) it's easy to accidentally overwrite one of your macros. All you have to do is accidentally press q
instead of @
and then all your hard work is gone. So I wrote a quick set of helper functions to easily save and restore useful macros:
"s:macro_file is where I have macros.json
function! s:macro_list(ArgLead, CmdLine, CursorPos)
let macros = json_decode(readfile(s:macro_file))
return join(keys(macros), "\n")
endfunction
function! s:GetMacro(macro_name)
let macros = json_decode(readfile(s:macro_file))
echom macros[a:macro_name]
endfunction
command! -nargs=1 -complete=custom,s:macro_list GetMacro call s:GetMacro(<f-args>)
function! s:SetMacro(macro_name, reg)
let macros = json_decode(readfile(s:macro_file))
call setreg(a:reg, macros[a:macro_name])
endfunction
command! -nargs=+ SetMacro call s:SetMacro(<f-args>)
function! s:SaveMacro(macro_name, reg)
let macros = json_decode(readfile(s:macro_file))
let macros[a:macro_name] = getreg(a:reg)
call writefile([json_encode(macros)], s:macro_file)
endfunction
command! -nargs=+ SaveMacro call s:SaveMacro(<f-args>)
What I really like about this is discovering the -complete
flag, which lets me it add custom autocomplete to any vim command. With this it autocompletes any of the keys in the macro json. I'm looking forward to playing with autocompletes a lot more in the future; I already have some ideas on things I want to do.
If you're reading this on the web, you can subscribe here. Updates are once a week. My main website is here.
My new book, Logic for Programmers, is now in early access! Get it here.