Late binding is key to enhanced productivity in programming languages. I believe that this is the single most important reason why so-called dynamic typed languages are so popular.
This note is part of an ongoing ‘language design’ series which aims to look at some key aspects of programming language design.
Simply put, a programmer should not have to say more than they mean at any particular time.
To see what I mean, consider a function that computes a person's name from a first and last name. In Star, I can write this:
This constitutes a complete definition of the function: there is no need to declare types; furthermore this function will work with any type that has a first and last name.
Contract this with a typical well-crafted Java solution:
Not so different one might argue.
Except that we have had to define a type
In designing the Java version we have to find and/or create a type
This last aspect can be a real productivity killer. Suppose that we want to be able to compute the full name of many different kinds of objects; then we must arrange them all to implement the
One of the common perceptions is that you lose the safety of a type checker if you want to allow late binding. This is not true; at least, it is not true for Star.
Star is a statically typed language which supports a range of type constraints. In the case of the
These constraints can be checked; so, for example, every time the
It is even possible (necessary) to go one step further and allow generic functions to use the
Late binding shows up in other ways. For example, when specifying an imported library of some form or other, it should be possible to declare the requirement for a library in terms of what is needed, rather than the name of the library. I.e., instead of:
we should be able to say:
or some such expression.
The difference is that the former says — in the source code — which package to import whereas the latter merely declares a contract requirement. How the contract is fulfilled is a separate step; one that a smart compiler system may even be able to automate.
(Some language do work this way. Typically LISP language systems organize their modules in terms of requires and provides.)
It can be hard to know what features should go into a programming language. Having a few principles to guide us make the task of designing a language more tractable.
In this case, the message is that programmers should be able to program in terms of their requirements, not the compiler's. This need not come at the expense of safety or of performance.
This note is part of an ongoing ‘language design’ series which aims to look at some key aspects of programming language design.
What do we mean by late binding?
Simply put, a programmer should not have to say more than they mean at any particular time.
To see what I mean, consider a function that computes a person's name from a first and last name. In Star, I can write this:
fullName(P) is P.firstName()++P.lastName()
This constitutes a complete definition of the function: there is no need to declare types; furthermore this function will work with any type that has a first and last name.
Contract this with a typical well-crafted Java solution:
boolean fullName(Person P){
return P.firstName()+P.lastName();
}
Not so different one might argue.
Except that we have had to define a type
Person
; at best this is an interface and not a class. The Java fullName
method will only work with objects of type Person
.In designing the Java version we have to find and/or create a type
Person
. In addition, we must make sure that all classes that we want to compute the full name of implement the Person
type.This last aspect can be a real productivity killer. Suppose that we want to be able to compute the full name of many different kinds of objects; then we must arrange them all to implement the
Person
type. This may not even be possible if the classes in question are from a library that you don't have the source to.Late Binding does not mean dynamic typing
One of the common perceptions is that you lose the safety of a type checker if you want to allow late binding. This is not true; at least, it is not true for Star.
Star is a statically typed language which supports a range of type constraints. In the case of the
fullName
function, the constraint is that the type of P
has the firstName
and lastName
attributes. (The details of how this is done are too gory to go into here.) These constraints can be checked; so, for example, every time the
fullName
function is used on an argument, the checker can verify that the type of the argument is consistent with having a first and last name. This check can be performed at compile-time.It is even possible (necessary) to go one step further and allow generic functions to use the
fullName
function.Other forms of Late Binding
Late binding shows up in other ways. For example, when specifying an imported library of some form or other, it should be possible to declare the requirement for a library in terms of what is needed, rather than the name of the library. I.e., instead of:
import java.util.List;
import java.util.ArrayList;
we should be able to say:
require List of integer;
or some such expression.
The difference is that the former says — in the source code — which package to import whereas the latter merely declares a contract requirement. How the contract is fulfilled is a separate step; one that a smart compiler system may even be able to automate.
(Some language do work this way. Typically LISP language systems organize their modules in terms of requires and provides.)
Take Away
It can be hard to know what features should go into a programming language. Having a few principles to guide us make the task of designing a language more tractable.
In this case, the message is that programmers should be able to program in terms of their requirements, not the compiler's. This need not come at the expense of safety or of performance.