Induction, Coinduction, and Fixed Points in PL Type Theory

02/26/2019 ∙ by Moez A. AbdelGawad, et al. ∙ Rice University 0

Recently we presented a concise survey of the formulation of the induction and coinduction principles, and some concepts related to them, in programming languages type theory and four other mathematical disciplines. The presentation in type theory involved the separate formulation of these concepts, first, in the theory of types of functional programming languages and, next, in the theory of types of object-oriented programming languages. In this article we show that separating these two formulations helps demonstrate some of the fundamental differences between structural subtyping, predominant in functional programming languages, and nominal subtyping, predominant in object-oriented programming languages---including differences concerning type negation and concerning the existence of inductive types, of coinductive types, and of approximations thereof. In the article we also motivate mutual coinduction and mutual coinductive types, and their approximations, and we discuss in brief the potential relevance of these concepts to object-oriented programming (OOP) type theory.

READ FULL TEXT VIEW PDF
POST COMMENT

Comments

There are no comments yet.

Authors

This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

1 Introduction

Applications of the induction and coinduction principles, and of concepts related to them, in scientific fields are plenty, including ones in economics and econometrics [35], in mathematical physics [38], in computer science, and in many other areas of mathematics itself. Their applications in computer science, in particular, include ones in programming language semantics—which we touch upon in this article—as well as in relational database theory (e.g., recursive or iterated joins) and in concurrency theory.111For more details on the use of induction, coinduction, and fixed points in computer science, and for concrete examples of how they are used, the reader is invited to check relevant literature on the topic, e.g., [26, 39, 15, 42, 32].

Fixed points, induction and coinduction have been formulated and studied in various subfields of mathematics, usually using different vocabulary in each field. The interested reader is invited to check our concise comparative survey in [8].

Of particular interest to programming languages researchers, in this article we give attention to the fundamental conceptual difference between structural type theory, where type equality and type inclusion are defined based on type structures but not on type names, and nominal type theory, where type equality and type inclusion are defined based on the names of types in addition to their structures. Given their difference in how each particularly defines the inclusion relation between types (i.e., the subtyping relation), the conceptual difference between structural typing and nominal typing expresses itself, prominently, when fixed points and related concepts are formulated in each of structural type theory and nominal type theory.

As such, in this article we present the formulation of these concepts in the theory of types of functional programming languages (which is largely structurally-typed) in 2.1 (2.1 Inductive and Coinductive Functional Data Types), then we follow that by presenting their formulation in the type theory of object-oriented programming languages (which is largely nominally-typed) in 2.2 (2.2 Object-Oriented Type Theory).

In 7 of [8] we summarized the article by presenting tables that collect the formulations given in the article. Based on the tabular comparison in 7 of [8] and based on the discussions in 2 (and in 3 of [8]), we discuss in 3 (3 Structural Type Theory versus Nominal Type Theory) some of the fundamental differences between structural typing and nominal typing, particularly ones related to type negation and coinductive types, and we also discuss some consequences of these fundamental differences.

2 Programming Languages Theory

Given the ‘types as sets’ view of types in programming languages, this section builds on the set-theoretic presentation in 3 of [8] to present the induction and coinduction principles using the jargon of programming languages type theory. The presentation allows us to demonstrate and discuss the influence structural and nominal typing have on the theory of type systems of functional programming languages (which mostly use structural typing) and object-oriented programming languages (which mostly use nominal typing).

2.1 Inductive and Coinductive Functional Data Types

Formulation

Let be the set of structural types in functional programming.222By construction/definition, the poset of structural types under the inclusion/structural subtyping ordering relation is always a complete lattice. This point is discussed in more detail below.

Let (‘is a subset/subtype of’) denote the structural subtyping/inclusion relation between structural data types, and let (‘has type/is a member of/has structural property’) denote the structural typing relation between structural data values and structural data types.

Now, if is a polynomial (with powers) datatype constructor333That is, is one of the , , or data type constructors (i.e., the summation/disjoint-union/variant constructor, the product/record/labeled-product constructor, or the continuous-function/exponential/power constructor, respectively) or is a composition of these constructors. By their definitions in domain theory [43, 40, 27, 30, 12, 23, 19], these structural datatype constructors, and their compositions, are monotonic (also called covariant) datatype constructors (except for the first type argument of , for which is an anti-monotonic/contravariant constructor, but that otherwise “behaves nicely” [34])., i.e., if

then an inductively-defined type/set , the smallest -closed set, exists in , and is also the smallest fixed point of , and a coinductively-defined type/set , the largest -consistent set, exists in , and is also the largest fixed point of .444See Table 1 of [8] for the definitions of and .

Further, for any type (where , as a structural type, expresses a structural property of data values) we have:

  • (structural induction, and recursion)co
    (i.e., ),
    which, in words, means that if the (structural) property is preserved by (i.e., if is -closed), then all data values of the inductive type have property (i.e., ).
    Furthermore, borrowing terminology from category theory (see 6 of [8]), a recursive function that maps data values of the inductive type to data values of type (i.e., having structural property ) is the unique catamorphism (also called a fold) from to (where is viewed as an initial -algebra and as an -algebra), and

  • (structural coinduction, and corecursion)
    (i.e.,
    which, in words, means that if the (structural) property is reflected by (i.e., if is -consistent), then all data values that have property are data values of the coinductive type (i.e., ).
    Furthermore, borrowing terminology from category theory, a corecursive function that maps data values of type (i.e., having structural property ) to data values of the coinductive type is the unique anamorphism from to (where is viewed as an -coalgebra and as a final -coalgebra).

Notes

  • To guarantee the existence of and in for all type constructors , and hence to guarantee the ability to reason easily—i.e., inductively and coinductively—about functional programs, the domain of types in functional programming is deliberately constructed to be a complete lattice under the inclusion ordering. This is achieved by limiting the type constructors used in constructing and over to structural type constructors only (i.e., to the constructors , , and their compositions, in addition to basic types such as Unit, Bool, Top, Nat and Int).

    • For example, the inductive type of lists of integers in functional programming is defined structurally (i.e., using , , and structural induction) as

      which defines the type as (isomorphic/equivalent to) the summation of type Unit (which provides the value unit as an encoding for the empty list) to the product of type Int with type itself.

    • In fact the three basic types Bool, Nat and Int can also be defined structurally. For example, in a functional program we may structurally define type Bool using the definition (for false and true), structurally define type Nat using the definition (for 0 and the successor of a natural number), and, out of other equally-valid choices, structurally define type Int using the definition (for negative integers, zero, and positive integers).

References

See [39, 22, 15, 47].

2.2 Object-Oriented Type Theory

The accurate and precise understanding of the generic subtyping relation in mainstream OOP languages such as Java, C#, C++, Kotlin and Scala, and the proper mathematical modeling of the OO subtyping relation in these languages, is one of our main research interests. Due to the existence of features such as wildcard types, type erasure, and bounded generic classes (where classes555The notion of class in this article includes that of an abstract class, of an interface, and of an enum in Java [24]. It also includes similar “type-constructing” constructs in other nominally-typed OO languages, such as traits in Scala [36]. And a generic class is a class that takes a type parameter (An example is the generic interface List in Java—that models lists/sequences of items—whose type parameter specifies the type of items in a list).

play the role of type constructors), the mathematical modeling of the generic subtyping relation in mainstream OOP languages is a hard problem that, in spite of much effort, seems to still have not been resolved, at least not completely nor satisfactorily, up to the present moment 

[45, 25, 6, 4, 9, 7].

The majority of mainstream OO programming languages are class-based, and subtyping () is a fundamental relation in OO software development. In industrial-strength OOP, i.e., in statically-typed class-based OO programming languages such as Java, C#, C++, Kotlin and Scala, class names are used as type names, since class names—which objects carry at runtime—are assumed to be associated with behavioral class contracts by developers of OO software. Hence, the decision of equality between types in these languages takes type names in consideration—hence, nominal typing. In agreement with the nominality of typing in these OO languages, the fundamental subtyping relation in these languages is also a nominal relation. Accordingly, subtyping decisions in the type systems of these OO languages make use of the inherently-nominal inheritance declarations (i.e., that are explicitly declared between class names) in programs written using these languages.666Type/contract inheritance that we discuss in this article is the same thing as the inheritance of behavioral interfaces (APIs) from superclasses to their subclasses that mainstream OO software developers are familiar with. As is empirically familiar to OO developers, the subtyping relation in class-based OO programming languages is in one-to-one correspondence with API (and, thus, type/contract) inheritance from superclasses to their subclasses [46]. Formally, this correspondence is due to the nominality of the subtyping relation [2]. (For a more detailed overview of nominal typing versus structural typing in OOP see [1].)

Formulation

Let (‘is a subtype of’) denote the nominal subtyping relation between nominal data types (i.e., class types), and let (‘has type’) denote the nominal typing relation between nominal data values (i.e., objects) and nominal data types.

Further, let be the set of nominal types in object-oriented programming, ordered by the nominal subtyping relation, and let be a type constructor over (e.g., a generic class).777Unlike poset in 2.1 (of structural types under the structural subtyping relation), poset (of nominal types under the nominal subtyping relation) is not guaranteed to be a complete lattice.

A type is called an ‘-supertype’ if its -image is a subtype of it, i.e., if

and is said to be preserved by . (An -supertype is sometimes also called an -closed type, -lower bounded type, -large type, inductive type, or algebraic type). The root or top of the subtyping hierarchy, if it exists (in ), is usually called Object or All, and it is an -supertype for all generic classes . In fact the top type, when it exists, is the greatest -supertype for all .

A type is called an ‘-subtype’ if it is a subtype of its -image, i.e., if

and is said to be reflected by . (An -subtype is sometimes also called an -consistent type, -(upper) bounded type888From which comes the name -bounded generics in object-oriented programming., -small type, coinductive type, or coalgebraic type). The bottom of the subtyping hierarchy, if it exists (in ), is usually called Null or Nothing, and it is an -subtype for all generic classes . In fact the bottom type, when it exists, is the least -subtype for all .

A type is called a fixed point (or ‘fixed type’) of if it is equal to its -image, i.e., if

As such, a fixed point of is simultaneously an -supertype and an -subtype. (Such fixed types/points are rare in OOP practice).

Now, if is a covariant generic class (i.e., a types-generator)999Generic classes in Java are in fact always monotonic/covariant, not over types ordered by subtyping but over interval types ordered by containment. (See [10].) In particular, for any generic class in Java we have

meaning that if interval type is a subinterval-of (or contained-in) interval type then the instantiation of generic class with (i.e., the parameterized type , usually written as ) is always a subtype-of the instantiation of class with (i.e., of the parameterized type , usually written as ). As such, a generic class in Java is not exactly an endofunction over types but is rather what may be called an “indirect endofunction,” since it generates/constructs types not directly from types but from interval types that are themselves derived from types (see [9, 10]), and the generic class, as a function, is monotonic/covariant with respect to the containment relation over these interval types (i.e., it “generates a subtype when provided with a subinterval”). , i.e., if

and if , the ‘least -supertype’ exists in , and is also the least fixed point of , and if , the ‘greatest -subtype’, exists in , and is also the greatest fixed point of ,101010See Table 2 of [8] for the definitions of and in the (rare) case when happens to be a complete lattice. then, for any type we have:

  • (induction)co
    (i.e., ),
    which, in words, means that if the contract (i.e., behavioral type) is preserved by (i.e., is an -supertype), then the inductive type is a subtype of , and

  • (coinduction)
    (i.e., ),
    which, in words, means that if the contract (i.e., behavioral type) is reflected by (i.e., is an -subtype), then is a subtype of the coinductive type .

Notes

  • As discussed earlier and in , in structural type theory type expressions express only structural properties of data values, i.e., how the data values of the type are structured and constructed. In nominal type theory type names are associated with formal or informal contracts, called behavioral contracts, which express behavioral properties of the data values (e.g., objects) in addition to their structural properties.

    • To demonstrate, in a pure structural type system a record type that has, say, one member (e.g., type plane fly() , type bird fly() and type insect fly() ) is semantically equivalent to any other type that has the same member (i.e., type plane is equivalent to type bird and to type insect)—in other words, in a pure structural type system these types are ‘interchangeable for all purposes’.

    • On the other hand, in a pure nominal type system any types that have the same structure but have different names (e.g., types plane, bird and insect) are considered distinct types that are not semantically equivalent, since their different names (e.g., ‘plane’ versus ‘bird’ versus ‘insect’) imply the possibility, even likelihood, that data values of each type maintain different behavioral contracts, and thus of the likelihood of different use considerations for the types and their data values.111111For another example, a float used for monetary values (e.g., in finanicial transactions) should normally not be confused with (i.e., equated to) a float used for measuring distances (e.g., in scientific applications). Declaring type money=float type distance=float does not help in a purely structural type system, however, since the types float, money, and distance are structurally equivalent. On the other hand, in a purely nominal type system the declarations of types money and distance do have the desired effect, since the non-equivalence of the types is implied by their different names.,121212Further, when (1) the functional components of data values are (mutually) recursive, which is typical for methods of objects in OOP [5], and when (2) data values (i.e., objects) are autognostic data values (i.e., have a notion of self/this, which is an essential feature of mainstream OOP [20])—which are two features of OOP that necessitate recursive types—then the semantic differences between nominal typing and structural typing become even more prominent, since type names and their associated contracts gain more relevance as expressions of the richer recursive behavior of the more complex data values. (For more details, see [3] and [39, 19.3].)

  • In industrial-strength OO programming languages (such as Java, C#, C++, Kotlin and Scala) where types are nominal types rather than structural ones and, accordingly, where subtyping is a nominal relation, rarely is poset a lattice under the subtyping relation , let alone a complete lattice. Further, many type constructors (i.e., generic classes) in these languages are not covariant. As such, and rarely exist in .131313Annihilating the possibility of reasoning inductively or coinductively about nominal OO types. Still, the notion of a pre-fixed point (or of an -algebra) of a generic class and the notion of a post-fixed point (or of an -coalgebra) of , under the names -supertype and -subtype respectively, do have relevance in OO type theory, e.g., when discussing -bounded generics [7].141414In fact, owing to our research interests (hinted at in the beginning of 2.2), inquiries in [7] have been a main initial motivation for writing this note/article. In particular, we have noted that if is a generic class in a Java program then a role similar to the role played by the coinductive type is played by the wildcard type F<?>, since, by the subtyping rules of Java (discussed in [7], and illustrated vividly in earlier publications such as [9, 6]), every -subtype (i.e., every parameterized type constructed using —called an instantiation of —and every subtype thereof) is a subtype of the type F<?>. On the other hand, in Java there is not a non-Null type (not even type F<Null>; see [7]) that plays a role similar to the role played above by the inductive type (i.e., a type that is a subtype of all -supertypes, which are all instantiations of and all supertypes thereof). This means that in Java greatest post-fixed points (i.e., greatest -subtypes) that are not greatest fixed points do exist, while non-bottom least pre-fixed points (i.e., least -supertypes) do not exist. Also, since is rarely a complete lattice, greatest fixed points, generally-speaking, do not exist in Java, neither do least fixed points. These same observations apply more-or-less to other nominally-typed OOP languages similar to Java, such as C#, C++, Kotlin and Scala. (See further discussion in Footnote 18 in  of [8].)

  • While not immediately obvious (nor widely-known), but, as a set of pairs of types, the subtyping relation in nominally-typed OOP is in fact a coinductive set, i.e., is a coinductively-defined subset of the set of all pairs of class types [39, Ch. 21]. That is because subtyping between two types holds in nominally-typed OOP as long as there is no (finite) reason for it not to hold. The following is in fact how javac, the standard Java compiler, type-checks Java programs: During type checking javac assumes that a subtyping relation between two given types holds unless the type checker can (finitely) prove, using the explicitly specified subtyping declarations in a Java program, that the relation cannot hold.151515The subtyping relation in Java seems to be even a little bit more complex than a coinductive set. Due to the existence of wildcard/interval types, the Java subtyping relation, together with the containment relation between wildcard/interval types  [10], seems to be an instance of a mutually coinductive set (i.e., the coinductive counterpart of a mutually inductive set, which we did not get to discuss in this article but may do in the future.)

    • In particular, to the best of our knowledge, the Java language specification does not stipulate that the subtyping relation holds between two types only if the relation can be (finitely) proven to hold between the two types (see [24, 4.10]). This missing “disclaimer”, which is usually stipulated in the definition of similar relations but that seems to be intentionally missing in the definition of the subtyping relation between class types (a.k.a., reference types) in Java, allows for the subtyping relation in Java to be coinductive.

References

See [18, 17, 16, 14, 22, 31, 45, 7].

3 Structural Type Theory versus Nominal Type Theory

3.1 Existence of Fixed Points

The discussion in 2.1, together with that in 3 and 5 of [8], demonstrates that FP type theory, with its structural types and structural subtyping rules being motivated by mathematical reasoning about programs (using induction or coinduction), is closer in its flavor to set theory (and first-order logic/predicate calculus), since structural type theory assumes and requires the existence of fixed points and in for all type constructors . (For a discussion of the importance of structural typing in FP see [29, 33] and [39, 19.3].)

On the other hand, the discussion in 2.2, together with that in 6 and 2 of [8], demonstrates that OOP type theory, with its nominal types and nominal subtyping being motivated by the association of nominal types with behavioral contracts, is closer in its flavor to category theory and order theory, since nominal type theory does not assume or require the existence of fixed points and in for all type constructors . (For a discussion of why nominal typing and nominal subtyping matter in OOP see [3] and [39, 19.3].)

As such, we conclude that the theory of data types of functional programming languages is more similar in its views and its flavor to the views and flavor of set theory and first-order logic, while the theory of data types of object-oriented programming languages is more similar in its views and its flavor to those of category theory and order theory. This conclusion adds further supporting evidence to our speculation (e.g., in [6, 4]) that category theory is more suited than set theory for the accurate understanding of mainstream object-oriented type systems.

3.2 Type Negation, Coinductive Nominal Types, and Free Types

In 3 of [11] we noted that negation of a mathematical object (e.g., a logical statement, a set, or a type) is useful in defining coinductive objects. We also noted that negation in set theory amounts to set complementation, which is useful in defining coinductive sets in terms of inductive sets. We wondered also about defining negation in other categories, such as the category of structural types (ordered by structural subtyping) in functional programming languages and the category of nominal types (ordered by nominal subtyping) in object-oriented programming languages. We resume this discussion here.

In object-oriented programming for example (see 2.2), we may try to define negation of a class type as follows. First, let’s define an “implication” type (which may also be called an ‘exponential type’) from type to type as

(1)

i.e., as the join of all types whose meet with type is a subtype of type ,161616Formula (1) comes from the study of Heyting algebras (which model constructive/intuitionistic logic). The formula is valid for defining complementation in set theory (where join is interpreted as set union , meet is interpreted as set intersection , is interpreted as set inclusion , and is interpreted as the empty set ), since the inclusion lattice in set theory is a Boolean algebra and every Boolean algebra is a Heyting algebra. As such, in set theory the complement of a set (as a negation of ) can also be defined as

i.e., as the union of all subsets of that are disjoint with respect to . This definition, although it does not (explicitly) mention , is equivalent to the standard definition of set complementation, namely the definition
then (as done in functional programming) let’s define negation of a type as

which in other words means defining type negation as

i.e., defining the negation of a type as the join of all types parallel to the type,171717The term parallel here is used in an order-theoretic sense. In an ordered set two elements are parallel if neither holds nor holds (i.e., and are “independent elements” of ). This is usually denoted by writing  [21, 28, 41]. (Although not widely-known, but LaTeX has a command \parallel for inputting the symbol ). or more precisely as the join (“lub”) of those types whose meet with the type is the bottom type.

As such, noting (see 2.2) that actually denotes type Null (sometimes also called Nothing or Void) and denotes type Object (or All) then the negation of is (since is the only type whose meet with is equal to ), and the negation of is (since the meet of and every type, including , is ).

However, due to the general lack of (true) union types in most OOP languages181818In fact the lub() function—defined in the Java language specification—does not compute the least upper bound of a set of types (which itself is an approximation of the true union of the set of types) but it ‘only approximates a least upper bound’ [24, 4.10.4]., for most types (i.e., ones other than and ) the negation of a nominal type will usually be type . This makes the negation of class types, if defined as suggested above, not quite interesting or useful. (Also, as observed in 2.2, the subtyping relation in mainstream OOP languages rarely has fixed points of type generators/constructors).

An alternative way to defining the negation of a type in OOP in a more genuine object-oriented way (i.e., not via defining implication/exponential types) is to allow OO developers to choose which types they wish to negate, rather than trying to define a negation “automatically” for every type. An OO developer may define a “negation” of a type (in a genuine OO way, if they wished to have such a negative type) by them simply extending the negative type from a supertype of the negated type (or from one of its supertypes, depending on which part of the inherited behavioral contract is being negated).

For example, say we have class Window that is extended by class ColoredWindow. An OO developer can define the negation of ColoredWindow by declaring a class NonColoredWindow that extends class Window. (As is standard in nominal typing, it is up to the developer to ensure that a NonColoredWindow is indeed not colored, and in fact also that a ColoredWindow is indeed one.)

Negative types, according to this genuine nominal/OO way of defining them, are totally under the control of software developers, unlike the case in structural/FP typing. It may be noted also that type negation in a genuine OO way is not exclusive. For example, generally-speaking there is nothing that prevents the OO developer from declaring yet a third subclass of class Window that is not a ColoredWindow yet is also not a NonColoredWindow. (That third class may or may not be useful. The option of declaring it, or not, is available to the developer however. In other words, it is the developer, not the language, who makes the decision as to defining the class or not, based on his or her need for the class.)

The more genuinely OO way of defining negative types presented above, arguably, is better than the FP/structural way (via ), since it gives more control and offers more flexibility to developers. It should be noted, though, that there is a similarity between both ways: they both define type negation by depending (directly, in OOP, and indirectly, in FP) on ‘parallel types’ (see Footnote 17).

Having considered negative nominal types, now what about coinductive nominal types? Understanding these types (as counterparts of coinductive structural types in structural type theory and of coinductive sets in set theory) was the initial motivation for us considering type negation in the first place (see 3 of [11]). The starting point for considering such (strange-named, but familiar we assert) types will be Equation (1) of [11], which, let’s recall, is the equation that defines coinductive sets (in terms of inductive ones and negation) as

Equation (1) uses three negations, and we have just defined an intuitive notion of type negation for nominal types. Our view of coinductive nominal types will be a combination of both. In particular, we take each negation in Equation (1) separately to define a negative nominal type (that may or may not exist). As such, for a particular generic class (i.e., is a nominal type generator) we define its corresponding coinductive nominal type (if it exists) as the negative of (an approximation of, since nominal types do not have fixed points) the inductive type of a “dual” generic class (which, also, may or may not exist) that is applied to negations of types (if all these negations exist) that can be passed to the generic class as type arguments (!!).

Sounds too complex? (Due to the three negations?) Rest assured, in conclusion, and to make a long story short, intuitively-speaking the sought after coinductive nominal type is roughly (i.e., is approximated by) the familiar type —an observation we earlier made (in Footnote 14) without any discussion of type negation. In other words, we have

(again due to the absence of fixed points of generic classes in nominal type theory, a true coinductive nominal type in fact never exists). The type is also called the ‘free type’ corresponding to the generic class .191919The name ‘free type’ comes from category theory. The concept of a free type corresponding to a generic class is similar (in a precise category theoretic sense) to the free monoid corresponding to a set and to the free category (a quiver) corresponding to a graph [44]. The free type corresponding to a generic class is defined as the parameterized type formed by instantiating the generic class with the wildcard type ?. For example, the free type corresponding to generic class List is the type List<?>. Free types and (Java) type erasure both form an adjunction—they are a pair of adjoint functors between ‘classes & subclassing’ and ‘types & subtyping’. (In order theory, an adjunction is also called a Galois connection. See [4] for more details on this adjunction.) Based on the discussion in the main text, free types and coinductive nominal types agree, i.e., are the same concept. More precisely, free types are the best approximation of (true) coinductive nominal types. In category-theoretic terms free types are, precisely, instances of final coalgebras. (See Table 2 of [8].)

In light of the discussion (in 3 of [11]) of the intuitions behind coinductive sets (“good/con-structible/consistent vs. bad/inconstructible/inconsistent”, and finitely-constructible vs. infinitely-constructible) and that coinductive sets contain all elements (all data values, in case of coinductive structural data types) that can be constructed using a generator (i.e., ones that do not “break the rules” of the generator/constructor), it is not surprising that, for a generic class , the free type is the best approximation of the coinductive nominal type . That is because the free type is intuitively understood (e.g., by OO software developers and OO language designers) as the type that contains all objects that can be instances of (recall Java’s instanceof operator) the generic class (i.e., contains all objects that can be constructed using the generic class ). Further, in light of OOP not having a goal of inductive logical reasoning (i.e., finite structural reasoning) about objects, it is not surprising that the inductive nominal type is not supported (nor even an approximation for it) in most OOP languages. If defined, such a type or its approximation will contain only objects (in the OO sense, not the mathematical/category theoretic one) that can be finitely constructed using —which is not of much value (actually, makes little sense) to most OO software developers202020That is, to developers and designers of industrial-strength and mainstream OO software, who are content as long as a type that contains all objects that can be constructed using (regardless of whether they can be constructed “finitely or infinitely”)—namely the free type —is defined..

3.3 Structural Induction, Nominal Coinduction, and The Future

The discussion regarding coinductive nominal types and their approximations, combined with our observation regarding the coinductiveness of the subtyping relation (as a set of pairs) in nominal type systems (see notes of 2.2), make us conclude that fundamental differences exist between structural type theory (in FP) and nominal type theory (in OOP) regarding, first, the existence (in FP) versus nonexistence (in OOP) of fixed points (and pre-/post-fixed points) of type constructors (in FP) and generic classes (in OOP) and, second, regarding the dominance of (singular) induction and inductive definitions (in FP) versus the dominance of (mutual) coinduction and coinductive definitions (in OOP).

In light of the existence of circularity and recursive definitions in OOP at multiple levels, i.e., at the level of values (e.g., via this) and at the level of types (e.g., between class definitions, and noting the circular dependency of parameterized types on interval types and vice versa), and in light of other work also discussing coinductive definitions and the use of coinduction in OOP and in nominal type systems (e.g., see [39, p.312] and [13, 45]), we observe that:

“Induction reigns supreme” in functional programming, while in object-oriented programming “coinduction reigns supreme”.

This observation should not be viewed as a mathematical or academic statement but rather as an expression of practically-motivated fundamental features of OOP and FP. The main practical value of functional programming and the main motivation behind its very existence and behind its continued usage is to enable precise mathematical reasoning about software. The main practical value of object-oriented programming and the main motivation behind its existence and behind its continued usage is the accurate and intuitive modeling of typically highly-interconnected real or imaginary parts of our world. We, humans, know well how to reason, mathematically, about objects built up from few basic ones (i.e., inductively-defined objects), yet we also need software to accurately simulate and model parts of our interrelated world—one in which, to overcome circularity and interrelatedness, we are used to holding statements about objects as facts as long as these statements cannot be disproven (i.e., is an inherently coinductive world).

As such, the co-existence of FP (with its mainly inductive type systems) and OOP (with its mainly coinductive type systems), and our continued need for both, is a reflection of our reality, i.e., of what we currently know how to do and of what we currently need to do. It is our opinion that this current state of affairs is not a perpetual one, but is one that particularly invites for the further development of the mathematical methods used to reason about mutually coinductive definitions (and, more generally and more precisely, about mutually-defined post-fixed points) to reach or exceed the same level of mathematical maturity of the mathematical methods and tools used to reason about singular inductive definitions (i.e., about least fixed points). In summary, the current state of affairs invites PL theorists and researchers to make clear and transparent what is currently totally opaque,212121See, for example, the “totally opaque” code on p.58 of [37]—a standard reference on practical functional programming—which nevertheless has a strong object-oriented flavor! thereby elegantly combining the accurate mathematical-reasoning benefits of functional programming with the accurate world-modeling benefits of object-oriented programming.

References