Fallback duck typing in pocket-lang
In case you haven’t heard, I’ve embarked on an early stage project to create a modern programming language that’s compact, efficient, and incredibly practical. Its working name is pocket. In this post I’ll touch on a nifty feature of pocket, called fallback duck typing, that helps the language do what it needs to do.

If I had to summarize the end benefits of fallback duck typing, I’d say this: Fallback duck typing allows you to write code at an extremely high level, close to pseudocode, while getting C-like performance and Java-like safety 99% of the time.
That’s admittedly a grandiose claim. To understand fallback duck typing, let’s first have a quick refresher on traditional statically and dynamically typed programming languages.
In general, a language’s typing system can be placed into one of two camps: static or dynamic. In a static typing system, the types of every variable are assigned and fixed at compile time. A dynamic language relaxes this restriction, delegating type-dependent operations to the runtime.
There are benefits to both systems, which is likely why both are in widespread use today. Java is a popular statically typed language, and Python is a popular dynamically typed one. A recent popularity survey can be found here: https://stackify.com/popular-programming-languages-2018/ . Currently, it seems that statically typed languages (Java, C, C++) are edging out dynamically typed languages, but in general there is no clear winner.
The argument for static types (and it’s a good one) goes something like this: “Well, if I restrict the types of my variables, I can more easily understand what my code does, since I have to consider less possibilities. Also, my code runs a LOT faster, since the runtime doesn’t have to compute what operations mean everytime an operation is performed.” This is borne out by speed tests: see https://benchmarksgame-team.pages.debian.net/benchmarksgame/which-programs-are-fast.html for an illustration. C is 100x faster than Python for certain tasks. One hundred times faster. That’s serious.
The argument for dynamic types (and it’s equally compelling to some) goes like this: “I’ll admit that my code can run slower, but that doesn’t matter for 99% of applications, since computers are so darned fast these days. What I care about is development speed, which is clearly faster for dynamic typing, since I don’t have to write a boatload of type annotations on every variable and function, ever. In addition to much “cleaner looking code” (just take a look at the source code of your favorite C++ program, buddy), dynamic languages also allow for the concise expression of overloaded functions. What’s not to love!?”. Fair’s fair.
Clearly, these are both legitimate arguments, but what fewer are asking is: “Is there a way to combine the two camps: to get the speed and safety of static typing with the convenience and conciseness of dynamic typing? And if so, would it be worth it?”
I think the answer is yes and yes, and that’s something I’m emphasizing in pocket.
Disclaimer: to be clear, I’m not trying to say that no one has attempted anything like this before. See, for example, this, this, or this for an illustration of some attempts to make dynamically typed languages more “static-like”, or vice versa. There are benefits to designing a language that supports this from the ground-up, rather than tacking type systems onto existing languages.
Anyways, what’s the philosophy here?
Smart type inference that does what you want 99% of the time, but knows when to say “screw it, you’re a duck”.
Let’s make this more concrete. Imagine we need to add up all of the numbers in a list. Let’s consider a couple of universes. In the first universe, pocket is a traditional static language with type annotations. The sum program might look something like this:
xs list int : (list int)[1, 2, 3]
sum int : 0
for x int in xs
sum +: x
print sumOk, fine. That would run as fast as the CPU could handle. But it’s a bit sloppy looking. A lot of list int and int declarations scattered around.
Now let’s consider the other universe. In this second universe, pocket is implemented as a dynamically typed language. Here’s the same program:
xs : [1, 2, 3]
sum : 0
for x in xs
sum +: x
print sumNow, that’s looking nice! It’s hard to think of a way the readability of the above program could be improved. However, in a (naive) runtime implementation, the above code could take 10x longer to run than the statically typed version. Instead of just computing a single add in the loop body, the dynamic runtime has to first realize that it’s adding an int to an int, look up how to add ints to ints, and then finally perform the actual computation. Those dynamic lookups cost valuable CPU cycles and cache accesses. Compare that to the static add, which would execute directly on the registers in less than one CPU cycle (in a throughput sense) on all modern CPUs.
But there’s no reason it has to be this way. It’s intuitively obvious that there’s a way to transform the second program into the first. If we did that, we could write code in the second universe but run it in the first. That would be the best of both worlds, and it’d be just grand.
As a human, it’s easy to accomplish this. Just write in the known types of all the variables. But that’d defeat the human effort saved by writing such concise code. We need to automate this task of filling in the types. As a compiler that’s pretty tough to do, but it’s possible. It’s a known problem, called type inference.
There’s a slight rub with type inference. In general, it’s computationally undecidable! Basically, there’s no algorithm that’ll be able to fill in the correct types for every program. But luckily, that doesn’t matter for 99% of practical cases.
Pocket is practical. It handles the 99% concisely at the expense of the 1%. How? The high-level approach I’ve taken is this: allow programs to be written using duck type semantics (with optional explicit, enforced annotations), but try hard to prevent duck types leaking into the runtime. This is the essence of fallback duck typing. If the pocket compiler could explain, it’d say something like this:
“At compile time, I’m going to try my best to reduce your input program to a statically typed program. That’s because if I succeed, I can make it go zoom at runtime, plus give you a bunch of compile-time guarantees. However, I’m smart enough to realize when I can and can’t make progress. If I can’t figure out the type of an expression now, I’ll use fallback duck typing, marking the expression as a duck. At that point, it’ll be the runtime’s responsibility. I know this makes for a lot of awkward work and edge cases as a compiler and runtime system. However, if I can save the hooman just six keystrokes, it’s worth it.”
Hopefully that makes some sense. The compiler’s time has zero value compared to a human’s.
To finish off, let’s look at a real pocket program that demonstrates fallback duck typing. We’ll start with a program that can be fully statically inferenced:
main func
doThang 3doThang func(x int) void
y : 1 + x
print y
The generated Go code for the above program is:
package mainimport "fmt"func main() {
doThang(int64(3))
}
func doThang(x int64) {
var y int64
y = ((int64(1)+x))
fmt.Println(y)
}
Notice the assignment to yuses a standard addition on two Go int64values (quick and safe). Now, let’s look at an equivalent program that can’t be fully inferenced. Note the lack of parameter type declaration on the doThang function.
main func
doThang 3doThang func(x) void
y : 1 + x
print y
This version relies on duck typing to perform the 1+x , since x could be any type (it’s a public parameter, after all). Here’s the generated code for this version:
package mainimport "fmt"func main() {
sub(int64(3))
}
func sub(x interface{}) {
var y interface{}
y = (Pduck_add(int64(1),x))
fmt.Println(y)
}
Where Pduck_add refers to the runtime function that performs duck addition on two arbitrary Go interface{} types.
In either case, the output of the program is (rightly) the same:
4I hope you find fallback duck typing as cool as I do. Thanks for reading. Please comment if you have links to interesting references, etc.
Stay tuned to this Medium blog for progress updates on pocket development.
Please give me a Github star or file an issue on pocket-lang if you found this post interesting:
