It ain’t what you think it’s the language that you think in

Keir Finlow-Bates
6 min readJul 17, 2023

A few weeks ago I was talking to Michal, who is a Lead Security Engineer over at Resonance Security. We were planning to discuss cross-chain issues, but as is always the case, the conversation meandered off into different fields, mostly concerning how the programming language you use shapes your view of the world.

Theory of mind

There is a theory of mind based on language with a name that sounds like it came straight out of Star Trek.

The Sapir-Whorf hypothesis.

Simply put, it proposes that the grammar and vocabulary of the language we think in, and the society that we live in and which spawned that language, profoundly influence the way we interpret the world around us. Together, both frame and limit what we are able to mentally conjecture about.

“We cut nature up, organize it into concepts, and ascribe significances as we do, largely because we are parties to an agreement to organize it in this way — an agreement that holds throughout our speech community and is codified in the patterns of our language.”
— Benjamin Lee Whorf [1]

Wittgenstein, arguably the foremost philosopher of the 20th century (that is to say, his friends and colleagues argued that he was the foremost philosopher of the 20th century), put in a few simple albeit somewhat inscrutable words:

If a lion could speak, we could not understand him
— Ludwig Wittgenstein [2]

Nobody truly understands Wittgenstein though, possibly because he thought in an Austrian version of the German language, and then translated his thoughts into English.

function thoughts(language) { …

The thing that springs to the mind of any inquisitive software developer reading about these kinds of things is to wonder, “Does the programming language I choose to program in significantly affect the way I think about the tasks at hand? Am I restricting myself by only writing in C++?”

(The answer to both those questions is, “Probably, yes.”)

You wouldn’t be the first to have such thoughts. Here’s what Paul Graham said about it, a couple of decades ago:

“[programmers are] satisfied with with whatever language they happen to use, because it dictates the way they think about programs”
— Paul Graham [3]

But it’s not just programmers who are affected by this.

If you are a test engineer or an auditor, and you are reviewing smart contracts, the problem of programming language dissonance can be especially acute. You may be using one language, say, JavaScript, to write tests for smart contracts written in another language, for example, Solidity.

This involves exhausting context switching as you shift your focus from one programming language to another, and tired test engineers are always tempted to cut corners. Worse still, it can restrict your thought patterns and cause you to miss some significant areas of testing.

The imperative mood

JavaScript and Solidity are syntactically not that different. They are both imperative languages — abstractions of assembly language. And their syntax is very similar; it’s like comparing Spanish to Italian. But there are some notable problems. For example, when passing a value from JavaScript to a Solidity smart contract using Hardhat, you are often going to rely on your framework implicitly converting a JavaScript string to Solidity bytes. The behind-the-scenes conversion can cause bugs to slip through the cracks of your test coverage.

The disparity becomes even greater when you are working across two imperative programming languages with a different fundamental raison d’être.

For example Rust, which is compiled and requires an understanding of the underlying mechanics of memory allocation and reclamation, versus Python, which is more of a “get things done quickly” interpreted language with automatic garbage collection. In this case, we are looking at the programming equivalent of comparing German to French.

At least they’re both Indo-European languages though.

Most of the programmers I have worked with code in a predominantly imperative style, with control flow forming the backbone of their code.

But there is another significant class of programming languages: purely functional ones, such as Lisp and Haskell. If you are using JavaScript to test a Cardano smart contract in the Haskell-like language Plutus, then you are doing the coding equivalent of switching between German (an Indo-European language) and Finnish (a Finno-Ugric language with a distinctly different set of grammar rules, even if it does use the same alphabet).

Frameworks

Michal uses Foundry to write and test Ethereum smart contracts, whereas I am an adherent to the Hardhat toolchain for those activities. That means that he is writing his tests in Solidity, and I am writing in JavaScript. And as we’re both testing Solidity contracts, he doesn’t have to context switch and I do.

If you have the time and budget, ideally you’d hire a version of Michal and one of me to check your contract — there will be a lot of duplication, but the overlap of our testing will not be 100%.

Part of that is because we’re coding and thinking in different languages.

But there is another aspect worth considering.

Society as a framework

The problem with Solidity is that all the really serious bugs are environment related, not code related. What that means is that they depend as much on the EVM — the way data is stored and executed on Ethereum, as they do on lines of code.

This is like looking at a society’s behaviour rather than just its language. To use a cultural analogy: being able to speak Finnish is not going to teach you that you are expected to take your shoes off when entering a house in Finland.

The unfortunate conclusion is that to truly test a smart contract thoroughly, you need to fluent not just in the language of the contract, but also in the environment or “culture” that exists around that contract. And this will include a testing framework.

It is also why bridges from one chain to another are so vulnerable. There you are moving not just from one language to another, but from one culture to another as well. A general adage in the business of translating books is that your translator should be a native speaker of the language into which the book is being translated, but they need to be fluent in the language it is coming from.

In blockchain, that’s really hard. There are very few people who are native in one chain, and fluent in another. To be frank, there are very few people who are fluent in any given chain, let alone native.

Bridges should really be written, and then tested, by people who are fully conversant with both chains.

Conclusion

As part of the regular process of talking to the Resonance Security engineers, I am getting to exchange ideas with people from different backgrounds, who think in different languages, and write code in different languages too.

This week I was talking to Michal, who is Polish, and predominantly uses Rust. Last week I was in conversation with Joao, who is Portuguese, and prefers Python. And next week I’m going to be interviewing Luis, who informs me that he thinks in Spanish. I’ll find out what his favorite programming language is in due course.

That’s a lot of languages, and therefore a lot of different perspectives.

Given enough eyeballs, all bugs are shallow[4], and the same may well apply to software vulnerabilities and auditing too.

But it doesn’t work if all the eyeballs are the same…

References

[1] B. L. Whorf (1956): Language, Thought and Reality (ed. J. B. Carroll). Cambridge, MA: MIT Press

[2] L. Wittgenstein (1968), Philosophical Investigations (trans. G. E. M. Anscombe). New York: Macmillan

[3] P. Graham (2001), Beating the Averages. Retrieved from https://www.cs.tufts.edu/comp/150FP/archive/paul-graham/sec.pdf

[4] E. S. Raymond (2000), “Linus’s Law”, retrieved from http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html

--

--