Friday, October 9, 2026

Polymorphism is sets behind the scenes

Polymorphism in programming is the use of a single name to mean multiple (hopefully related) things. For example, you can generally use the symbol ‘+’ to add two integers, two reals (floats), or even an integer and a real number. Thus, the ‘+’ function is polymorphic over integers and reals. Simple enough in concept. But how do you implement this capability?

You use non-polymorphic functions, and collect them together in a set with the common (polymorphic) name. The ‘+’ function is really a set of related functions: [addInts, addFloats, addIntFloat, addFloatInt]. When the language implementation (whether a compiler or interpreter) encounters the ‘+’ function, it examines the types of the arguments and selects the proper actual function.

This polymorphic function name, which has no actual implementation of its own, is called different things in different programming languages. It might be an interface. It might be an enum. It might be hidden behind multiple definitions of the same function name as a polymorphic function. It might even be hidden in plain sight inside a pattern matching function definition. But the way it works is the same. A common name dispatches to separate definitions depending upon the types of the arguments.  (Object Oriented languages generally dispatch on the type of the first argument.  Some languages, like Julia and Nim, use multiple dispatch, using the types of all the arguments.)

If you know the name of the implementations (assuming they are separate), and you can predict the types of the arguments you will have, you can bypass the types lookup and simply use the proper function definition. This is how compilers work. It’s also how just-in-time compilation works. It can even work in truly polymorphic languages, where there are no separate names, as long as the language can access and recall the identity of the correct underlying implementation. After all, the implementations that share a common name end up being entries in a list, and that list can be accessed by index.

The exact same principle works in type polymorphism, also known as abstract types. You can have a type Number, which is really the set [Int, Float]. So when you have a variable of type Number, it can contain either an Int or a Float, but not both at the same time.  Generic types work in a similar fashion, but the actual type of some argument gets named and used as a specifier for the types of other arguments.

Things get a little more complicated (and noticeably slower) when you have a polymorphic function dispatching (selecting which version to execute) on polymorphic types. But that ends up being a relatively straightforward iteration over possibilities, restricted by the actual types of the arguments.

No comments:

Post a Comment

I reserve the right to remove egregiously profane or abusive comments, spam, and anything else that really annoys me. Feel free to agree or disagree, but let's keep this reasonably civil.