Smarter Help: Our Programming Tools
In The End of Dynamic Languages, I argued that one of the gains of statically-typed languages is superior tooling.
Can the same be said for dynamic languages? It seems that the correct answer here is: it can improve.
I’ve been using rubocop and HoundCI. They are the wrong tools. For example, here are some of the errors Hound threw at me for a recent PR:
None of these are useful! They contribute nothing whatsoever to the quality of the code. It is candy for stylistic pedants—nothing more.
Well you can just disable those rules, you argue. Yes, you can, just muck with this 1000+ lines of configuration so that you can create strings with '
s instead of "
s.
This is a false sense of quality and accomplishment.
The fact that it checks in your PR worsens the problem. We need systems to help us at the code writing stage, not before merging into master.
What we need
The closer we can get our tools to work with us instead of against us, the better we will be.
Below is an example how hlint helps me refactor as I write code. First, I write this function that adds up the values of the list:
But hlint
finds a reduction:
And another:
Turns out sum
already exists:
What we need are tools that, more and more, understand intent.
And because Haskell is statically typed, you can search for things via the type. For example, if I need a function that flattens [[a]]
(a list of lists of a
s) into [a]
, I can search for that on Hoogle ([[a]] -> [a]
) and find the function concat :: [[a]] -> [a]
.
These tools today mirror the first batch of AI research: it’s mostly (if not all) symbolic reasoning.
Of course some work is being done outside of symbolic reasoning. We can use more than just types. Kite, for example, is interesting because clearly it’s parsing the code, but it also leverages and organizes the ocean of programming knowledge available online.
It’s clear that more we assist our tools, the more our tools can assist us. Clojure’s spec
can be a huge win because it adds symbolic metadata that a program can read and manipulate. Property-based testing (ala QuickCheck) also works together with types to convey information that types cannot; namely, the invariants (or property) of our functions.
But I don’t know how far you can take this without types. If you give me an untyped AST, there is not much I can do, other than perhaps memorize or regex for some known modules and their functions (aka auto-complete). But it won’t be able to do what hlint
can do, because it can’t manipulate that untyped AST in a sure way.
Underlying all of this is a realization that the programmer and the tools affect each other. If you experience, for example, the power of property-based testing, you will begin to write your programs in a property-testable way. If your type system guides you to writing correct programs, then you will accept its assistant. If they complain about you using '
instead of "
for strings, then, well, you should uninstall that tool.
The next generation of tools should combine symbolic, statistical and data mining techniques. We should take as much help as we can, from types and specifications and properties and data mining. The point is, we need help. And we need smarter help.