blob: 9f95735ba2c9fc323264127725dc4926f532c1e0 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.7.1">
<title>Nominal And Structural Typing</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- ************* Meta ************* -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<!-- ************* OpenGraph ************-->
<meta name="description" content="The Eclipse N4JS language and its IDE enable high-quality JavaScript development for large Node.js projects.">
<meta property="og:site_name" content="Eclipse N4JS"/>
<meta property="og:title" content="Eclipse N4JS Language and IDE"/>
<meta property="og:url" content="https://numberfour.github.io/n4js"/>
<meta property="og:description" content="The Eclipse N4JS language and its IDE enable high-quality JavaScript development for large Node.js projects."/>
<meta property="og:image" content="../images/n4js.png">
<!-- ************* Favicon ************-->
<link rel="icon" href="../images/favicon.ico" />
<link rel="icon" type="image/png" href="../images/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="../images/favicon-16x16.png" sizes="16x16" />
<!-- ************* Back-to-top JQuery ************* -->
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
<!-- ************* Prism.js Syntax Highlighter ******-->
<link href="../styles/prism.min.css" rel="stylesheet"/>
<script src="../scripts/prism.js"></script>
<!-- ************* Styles ************* -->
<link rel="stylesheet" type="text/css" href="../styles/n4js-adoc.css">
<!-- ****************** NavBar ****************** -->
<div id="menubar">
<div class="banner">
<a href="../index.html"><img id="logo" src="../images/n4js-logo.png" alt="Eclipse N4JS Language and IDE"></a>
</div>
<ul>
<li><a href="../downloads.html"></i>Download</a></li>
<li><a href="../community.html"></i>Community</a></li>
<li><a href="../userguides/index.html">Documentation</a></li>
</ul>
</div>
<button id="tocbutton">TOC</button>
</head>
<body class="book">
<div id="header">
</div>
<div id="content">
<h1 id="_nominal_and_structural_typing" class="sect0"><a class="link" href="#_nominal_and_structural_typing">Nominal And Structural Typing</a></h1>
<div class="openblock partintro">
<div class="content">
<div class="paragraph">
<div class="title">Nominal And Structural Typing</div>
<p>One of the core responsibilities of a type system is to decide if two given types
are <strong>type compatible</strong>, or if a type is a <strong>subtype</strong> of another type.
N4JS provides support for different strategies of checking whether two types are compatible,
namely nominal (as known from Java) and structural typing (as known from TypeScript).
Additionally it proivdes certain variations of structural typing to support typical
usages in ECMAScript.</p>
</div>
<div class="paragraph">
<p>A type T<sub>1</sub> is compatible with a type T<sub>2</sub> if,
roughly speaking, a value of type T<sub>1</sub> may be used as if it were a value of
type T<sub>2</sub>.
Therefore, if type T<sub>1</sub> is compatible to type T<sub>2</sub>, a value that is
known to be of type T<sub>1</sub>
may, for example, be assigned to a variable of type T<sub>2</sub> or may be passed as
an argument to a
function expecting a value of type T<sub>2</sub>.
There are two major classes of type systems that differ in how they decide on
type compatibility:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Nominal type systems.</p>
</li>
<li>
<p>Structural type systems.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Since N4JS provides both forms of typing, we briefly introduce each approach in
the coming sections before we show how they are combined in N4JS.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_nominal_typing"><a class="link" href="#_nominal_typing">Nominal Typing</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>In a <strong>nominal</strong>, or <strong>nominative</strong>, type system, two types
are deemded to be the same if they have the
<strong>same name</strong> and a type T<sub>1</sub> is deemed to be a (immediate) subtype of
a type T<sub>2</sub> if T<sub>1</sub>
is <strong>explicitly declared</strong> to be a subtype of T<sub>2</sub>.</p>
</div>
<div class="paragraph">
<p>In the following example, <code>Employee</code> is a subtype of <code>Person</code>
because it is declared as such using keyword "extends"
within its class declaration. Conversely, <code>Product</code> is not a subtype of
<code>Person</code> because it lacks such an "extends"
declaration.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">class Person {
public name: string;
}
class Employee extends Person {
public salary: number;
}
class Manager extends Employee { }
class Product {
public name: string;
public price: number;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The subtype relation is transitive and thus <code>Manager</code> is not just a subtype of
<code>Employee</code> but also of <code>Person</code>. <code>Product</code> is not a
subtype of <code>Person</code>, although it provides the same members.</p>
</div>
<div class="paragraph">
<p>Most mainstream object-oriented languages use nominal subtyping, for example C++, C#, Java, Objective-C.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_structural_typing"><a class="link" href="#_structural_typing">Structural Typing</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>In a <strong>structural</strong> type system, two types are deemed the same if they are of the <strong>same structure</strong>.
In other words, if they have the same public fields and methods of compatible type/signature. Similarly, a type
T<sub>1</sub> is deemed a subtype of a type T<sub>2</sub> if and only if T<sub>1</sub> has
all public members (of compatible type/signature) that T<sub>2</sub> has (but may have more).</p>
</div>
<div class="paragraph">
<p>In the example from the previous section, we said <code>Product</code> is not a (nominal) subtype
of Person. In a structural type system, however, <code>Product</code> would indeed be deemed a (structual)
subtype of <code>Person</code> because it has all of <code>Person</code>'s public members of compatible type (only field
name" in this case). The opposite is, in fact, not true: <code>Person</code> is not a subtype of <code>Product</code>
because it lacks <code>Product</code>'s field <code>price</code>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_comparison"><a class="link" href="#_comparison">Comparison</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Both classes of type systems have their <a href="http://lambda-the-ultimate.org/node/5286">own advantages and proponents</a>.
Nominal type systems
are usually said to provide more type safety and better maintainability whereas structual typing is mostly believed
to be more flexible. As a matter of fact, nominal typing <strong>is</strong> structural typing extended with an extra relation
explicitly declaring the subtype relation (like the <code>extends</code> clause). So the real question is: What are the
advantages and disadvantages of such an explicit relation?</p>
</div>
<div class="paragraph">
<p>Let&#8217;s assume you want to provide a framework or library as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">export public interface Identifiable {
public get name(): string
static check(identifiable: Identifiable): boolean {
return identifiable.name != 'anonymous';
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">class A {
public get name(): string { return 'John'; }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">import { Identifiable } from 'Identifiable';
class A implements Identifiable {
@Override
public get name(): string { return 'John'; }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>A client may use these classes as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">Identifiable.check(new A());</code></pre>
</div>
</div>
<div class="paragraph">
<p>This first example is only to demonstrate the conceptual differences. The
structural variant would cause an error in N4JS.</p>
</div>
<div class="sect2">
<h3 id="_maintainability"><a class="link" href="#_maintainability">Maintainability</a></h3>
<div class="paragraph">
<p>Everything works fine. But maybe you consider to rename <code>name</code> to <code>id</code>. Assume you have
thousands of classes and interfaces.
You start by renaming the function in the interface:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">export public interface Identifiable {
public get id(): string
// …
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>With structural typing, you won&#8217;t get any errors in your framework. You are satisfied with your code and ship
the new version. Alas! The client code will no longer work as you have forgotten to accordingly rename the
function in class <code>A</code>.</p>
</div>
<div class="paragraph">
<p>With nominal typing, you would have gotten errors in your framework code already ("Class A must implement
getter id." and "The getter name must implement a getter from an interface."). Instead of breaking the code
on the client side only, you find the problem in the framework code.
In large systems, this can be a complete lifesaver. Without this strict validation, you probably wouldn&#8217;t
dare to refactor your framework. Of course, you may still break the client code, but even then it is much
easier to pinpoint the problem.</p>
</div>
</div>
<div class="sect2">
<h3 id="_flexibility"><a class="link" href="#_flexibility">Flexibility</a></h3>
<div class="paragraph">
<p>Given the same code as in the previous example, assume that the client code also uses another framework
providing a class Person as in the very first example.</p>
</div>
<div class="paragraph">
<p>With structural typing, it is no problem to use the Person class with the function check since the Person
class provides a data field name. So the code inside the function would work perfectly when called with an
instance of Person.</p>
</div>
<div class="paragraph">
<p>This won&#8217;t work with nominal typing though. Since Person does not explicitly "implement" Identifiable,
there is no chance to call function check. This can be quite annoying, particularly if the client can change
neither your framework nor the person framework.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_combination_of_nominal_and_structural_typing_in_n4js"><a class="link" href="#_combination_of_nominal_and_structural_typing_in_n4js">Combination of Nominal and Structural Typing in N4JS</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Because both classes of type systems have their advantages and because structural typing is particularly
useful in the context of a dynamic language ecosystem as that of JavaScript, N4JS provides both
kinds of typing and aims to combine them in a seamless way.</p>
</div>
<div class="paragraph">
<p>N4JS uses nominal typing by default, but allows to switch to structural typing by way of special type
constructors using the tilde symbol. The switch can be done with either of the following:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Globally when defining a type. This then applies to all uses of the type throughout the code, referred
to as<strong>definition-site structural typing</strong></p>
</li>
<li>
<p>Locally when referring to an existing nominal type, referred to as <strong>use-site structural typing</strong>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If we combine the above examples, we can use nominal and structural typing in N4JS as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">export public interface Identifiable {
public get name(): string
static check(identifiable: ~Identifiable): boolean {
return identifiable.name != 'anonymous';
}
}
class A implements Identifiable {
@Override public get name(): string { return 'John'; }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>For the argument of method "check" we use a (use-site) structural
type by prefixing the type reference with a ~ (tilde), which means
we are allowed, in the last line, to pass in an instance of <code>Product</code>
even though <code>Product</code> is not a nominal subtype of <code>Identifiable</code>.</p>
</div>
<div class="paragraph">
<p>This way, N4JS provides the advantages of nominal typing (which is the default form of typing)
while granting many of the advantages of structural typing if the programmer so chooses.
Additionally, if you would rename name to id, the tilde would tell you that there may be client code calling
the method with a structural type.</p>
</div>
<div class="paragraph">
<p>The full flexibility of a purely structurally typed language, however, cannot be achieved with
this combination. For example, the client of an existing function that is declared to expect
an argument of a nominal type N is confined to nominal typing. They cannot choose to invoke
this function with an argument that is only a structural subtype of N (it would be a compile time
error). This would possibly be exactly the intention of the framework author in order to enable easier
refactoring later.
This is comparable to access modifiers which serve the same purpose.</p>
</div>
<div class="sect2">
<h3 id="_field_structural_typing"><a class="link" href="#_field_structural_typing">Field Structural Typing</a></h3>
<div class="paragraph">
<p>N4JS provides some variants of structural types. Usually two structural types are compatible, if they
provide the same properties, or in case of classes, public members. In ECMAScript we often only need to
access the fields. In N4JS, we can use <code>~~</code> to refer to the so called "field structural type".
Two field structural types are compatible, if they provide the same <code>public</code> fields - methods
are ignored in these cases. Actually, N4JS can do even more. There are several modifiers to further filter
the properties or members to be considered: <code>~r~</code> only considers getters or data fields,
<code><sub>w</sub></code> only setters and data fields. <code>~i~</code> is used for initializer parameters:
For every setter or (non-optional) data field in the type, the <code><sub>i</sub></code> -type needs to provide
at least a getter (or readable data field).</p>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
</div>
</div>
<div class="Grid social" style="color:#d5dfea">
<div class="Cell Cell--2-12 m-Cell--withMargin">
<h2>Quick Links</h2>
<ul>
<li><a href="../downloads.html">Download</a></li>
<li><a href="../userguides/index.html">Documentation</a></li>
<li><a href="https://github.com/eclipse/n4js/">Source</a></li>
<li><a href="https://github.com/eclipse/n4js/issues">Issues</a></li>
</ul>
</div>
<div class="Cell Cell--2-12 m-Cell--withMargin">
<br/><br/>
<ul>
<li><a href="https://www.eclipse.org/forums/index.php/f/365/">Forum</a></li>
<li><a href="http://n4js.blogspot.de/">Blog</a></li>
<li><a href="https://dev.eclipse.org/mailman/listinfo/n4js-dev">Mailing List</a></li>
<li><a href="https://projects.eclipse.org/projects/technology.n4js">Eclipse Project Page</a></li>
<li><a href="https://twitter.com/n4jsdev">Tweets by n4jsdev</a></li>
</ul>
</div>
<div class="Cell Cell--2-12 m-Cell--withMargin">
<br/><br/>
<ul>
<li><a href="http://www.eclipse.org/">Eclipse Home</a></li>
<li><a href="http://www.eclipse.org/legal/privacy.php">Privacy Policy</a></li>
<li><a href="http://www.eclipse.org/legal/termsofuse.php">Terms of Use</a></li>
<li><a href="http://www.eclipse.org/legal/copyright.php">Copyright Agent</a></li>
<li><a href="http://www.eclipse.org/legal/">Legal</a></li>
</ul>
</div>
<div style="clear: both; height: 0; overflow: hidden;"></div>
</div>
<script>
// Toggle the table of contents
$( "button#tocbutton" ).click(function() {
if ($("#tocbutton").css('right') == '25px') {
$( "#tocbutton" ).animate({right: '215px'},"slow");
$( "#toc.toc2" ).animate({right: '0'},"slow");
}
else {
$( "#tocbutton" ).animate({right: '25px'},"slow");
$( "#toc.toc2" ).animate({right: '-13rem'},"slow");
}
});
</script>
<script type="text/javascript">
// Create a back to top button
$('body').prepend('<a href="#" class="back-to-top">Back to Top</a>');
var amountScrolled = 300;
$(window).scroll(function() {
if ( $(window).scrollTop() > amountScrolled ) {
$('a.back-to-top').fadeIn('slow');
} else {
$('a.back-to-top').fadeOut('slow');
}
});
$('a.back-to-top, a.simple-back-to-top').click(function() {
$('html, body').animate({
scrollTop: 0
}, 700);
return false;
});
</script>
</body>
</html>