With dynamic typing, it is fairly straightforward to implement an object-oriented system in terms of closures. I've written about this in my answer to “Modelling Objects as Functions” and expanded on that on my blog. There are some details about open recursion that are necessary to get certain OOP features such as subclassing right, but it's doable. In short, invoking the closure representing an object maps a method name to a method bound to that object, which can then be invoked as an ordinary function. Usage might look like
result = object("methodName")(otherObject).
However, OOP and static typing are fundamentally at odds. There are various interpretations of OOP, such as OOP-as-message-passing or OOP-as-virtual-dispatch, but a central theme is that we do not statically know the exact runtime type of an expression such as
x. However, many statically typed OOP languages do assign type bounds to an expression (such as
x is a subtype of
Iterable). When implementing objects in terms of closures in a statically typed language, we quickly run into the problem – when specifying the required method as an argument to the dispatch function, we can't statically know the required signature of the function. In the above example, how do I know that the result of
object("methodName") should be a function accepting exactly one parameter?
The common solution when implementing objects is to make the object-system unityped (aka. untyped). That is, all method signatures would have the same type regardless of their arity (number of arguments) or of the types of arguments. This also requires that all our objects have the same type in the host language.
Some languages might be able to do better. E.g. in C++, we could pass the requested method type as a template parameter to the dispatch function:
auto result = object<ResultType(ArgumentType)>("methodName")(otherObject). A clever implementation could then use different lookup tables to dispatch
int(int) method types etc. However, we cannot statically guarantee that the requested method exists since it is provided as a string. Another possibility would be to pass in a (visitor-like?)
Message object rather than a string name to the dispatch function, which might allow more type safety in statically typed host languages, but I do not have sufficient experience with implementing OOP in static languages to describe this in more detail.