Est. 2000 Intermediate

Nice

Nice is the research language Daniel Bonniot built at INRIA Rocquencourt around the ML-Sub type system - a Java-syntax language for the JVM with multi-methods that dispatch on every argument, option types that make NullPointerException impossible by construction, parametric classes years before Java 5 generics, anonymous functions, tuples, named and optional parameters, abstract interfaces and design-by-contract - first published on SourceForge in October 2000, declared stable as 0.6 in July 2002, and last released as 0.9.13 in December 2007

Created by Daniel Bonniot, then a doctoral researcher in the Cristal project at INRIA Rocquencourt, France (PhD 'Typage modulaire des multi-méthodes', 2005, supervised by Didier Rémy). The type system implements the ML-Sub system of François Bourdoncle and Stephan Merz (POPL 1997), and the mlsub typing engine also carries the author tags of Alexandre Frey, whose 2004 thesis formalised an ML-derived language with subtyping and multi-methods. Bryn Keller co-wrote the user's manual, with contributions from Francis Barber; Arjan Boeijink and Luc Perrin are credited in the release notes for converting a large part of the compiler from Java into Nice; the bytecode back end is Per Bothner's gnu.bytecode from Kawa

Paradigm Object-oriented with multi-methods (methods are declared with a signature and implemented by separate, possibly externally-defined alternatives that dispatch on the run-time classes - or literal values - of all arguments) combined with functional programming (first-class anonymous functions, functional types such as int->boolean, foreach/map/foldLeft over collections, tuples) and design by contract (requires/ensures clauses and assertions)
Typing Static, strong, safe: the ML-Sub constraint-based type system with parametric polymorphism and subtyping, local type inference for let/var declarations and anonymous-function return types, option types (?String may be null, String may not) with nullness inference on local variables, non-covariant arrays, parametric classes and abstract interfaces, and no cast operator - the compiler guarantees that a Nice program can throw neither NullPointerException nor ClassCastException
First Appeared 2000 (the SourceForge project was registered on 12 October 2000 and the first downloadable compiler, Nice.tar, is dated 26 October 2000; the release-notes file starts with an 'Initial Release' dated 18 December 2001, version 0.5 followed in April 2002 and 0.6, the 'first stable version', in July 2002)
Latest Version 0.9.13 (release notes dated 25 November 2007; Debian upload 3 December 2007; Windows zip published on SourceForge 19 December 2007). No later release exists; the project's CVS commit list received its last message in April 2011

Nice is a statically typed language for the Java Virtual Machine that set out, around the turn of the millennium, to fix the things that Java programmers of the time had learned to live with: the NullPointerException, the ClassCastException waiting behind every downcast out of a raw Collection, the anonymous inner class needed to pass a callback, the overloaded methods written to simulate default arguments, and the Visitor pattern written to simulate dispatch on more than one argument. Its author, Daniel Bonniot of INRIA Rocquencourt, kept Java’s surface syntax almost intact and replaced the type system underneath with ML-Sub - a constraint-based system with parametric polymorphism and subtyping devised by François Bourdoncle and Stephan Merz - so that a Nice program could use generic collections, first-class functions, tuples, option types and multi-methods and still compile to ordinary class files that any JVM from 1.2 upward would run.

The language’s own presentation summarised the goal in three words - safety, modularity, expressivity - and made a claim few JVM languages have made before or since: “programs written in Nice never throw the infamous NullPointerException nor ClassCastException.” For a few years, between the “first stable version” 0.6 of July 2002 and the final release 0.9.13 of December 2007, Nice had a small but engaged community, a manual, an Eclipse plugin, a Debian package and a place in the academic literature on multiple dispatch. It never reached the 1.0 its roadmap promised, and it is dormant today, but it is a remarkably complete demonstration of ideas - non-nullable reference types, local type inference, open classes, value dispatch, design by contract on the JVM - that later languages adopted one by one.

History and origins

ML-Sub and the research setting

The academic lineage of Nice runs through the École des Mines de Paris and INRIA. In 1997 François Bourdoncle and Stephan Merz published “Type checking higher-order polymorphic multi-methods” at POPL, defining a type system - ML-Sub - in which multi-methods could be given polymorphic types with subtyping constraints and checked modularly. The Nice website’s research page describes that paper as presenting “the core type system, and proves its safety property”. Bonniot, working in the Cristal project at INRIA Rocquencourt under Didier Rémy, took the system as the basis for a practical language; his early web page called Nice “a research programming language” that “demonstrates how the powerful ML-Sub type system can be used in practice” and referred readers to his DEA graduation report for the rationale, because “there is no real manual for the moment”.

Two further papers supplied the theory for the language’s more unusual features. At FOOL 9 - the Ninth International Workshop on Foundations of Object-Oriented Languages, held in Portland on 19 January 2002 after POPL - Bonniot presented “Type-checking multi-methods in ML (a modular approach)”, which the research page says “simplified the original approach and introduced type inference” beyond what the compiler yet implemented. At the TIP'02 Workshop on Types in Programming he presented “Using kinds to type partially-polymorphic methods” (published in Electronic Notes in Theoretical Computer Science volume 75 in February 2003), which the site calls “the theoretical basis for abstract interfaces in Nice”. The same page notes that abstract interfaces “were originally called interfaces in the earlier Bossa language” - a name that survives in the compiler, whose front-end packages are still called bossa.syntax, bossa.modules and bossa.parser.

Bonniot’s doctoral thesis, “Typage modulaire des multi-méthodes”, is recorded by the French thesis registry as defended in 2005 (record 2005ENMP1337, which places it at the École des Mines de Paris) with Rémy as director. Its abstract describes “a modular type system for languages with multi-methods, compatible with a core language à la ML with subtyping and type inference”, presented algebraically so that the safety proof can be shared between extensions - the formalisation of multi-methods as constants of the core language, and the extension of ML-Sub with kinding constraints for “partially polymorphic” methods - before giving “an overview of the implementation of all those ideas in a complete language, Nice”. Alexandre Frey, whose author tags appear on sixteen files of the compiler’s mlsub typing engine, defended a closely related thesis on 18 June 2004, “Approche algébrique du typage d’un langage à la ML avec objets, sous-typage et multi-méthodes”.

From snapshots to stable releases

The public history of the implementation is unusually well documented, because SourceForge still lists every file Bonniot uploaded. The project was registered on 12 October 2000; the first tarball, simply Nice.tar, is dated 26 October 2000; the single earliest message on the nice-info mailing list is from the same month. Through 2001 the compiler was released as date-stamped snapshots - Nice-2001.04.06.tar, Nice-2001.06.06.jar, Nice-2001.11.23.tar - with a Debian package appearing alongside the 18 December 2001 snapshot that the release-notes file records as the “Initial Release”. The early site is candid about the state of things: the Emacs mode was the recommended editor, installation meant untarring into /usr, and visibility modifiers were “accepted for class members, but are currently ignored”.

Numbered versions began with 0.5 on 18 April 2002. The 0.5.x series, released every two or three weeks, fixed the shape of the language that users would recognise: functional types written int->int rather than fun(int)(int), an automatic constructor taking field values as named arguments (0.5.1), the terse anonymous-function syntax int i => i+1 and the renaming of iter to foreach (0.5.4), and a reentrant compiler that IDEs and build tools could call repeatedly (0.5.5). On 11 July 2002 Bonniot declared 0.6 the “first stable version, based on 0.5.6”, and five days later opened the 0.7 development series.

The 0.7 series (July 2002 to April 2003) is where Nice absorbed the Java it was extending and added the features that defined it. Version 0.7.4 (1 October 2002) added “support for Generic Java types”, so that the standard java.util collections could be used as List<String> - nearly two years before Java 5 shipped generics in September 2004. Version 0.7.5 (20 November 2002) added design by contract, “assertions, pre- and post-conditions”, together with the || operator on optional values. Versions 0.7.7 and 0.7.9 (March and April 2003) added dispatch on literal values and let implementations of Java-declared methods such as equals dispatch on every argument - “so now we can at last write equals(that@A) = this.x == that.x”, the release notes say. Version 0.8, the second stable release, was dated 14 May 2003.

The 0.9 years

The 0.9 series, starting on 18 July 2003, was meant to be the run-up to 1.0. The roadmap listed what remained: “improve the support for object creation, besides automatic constructors”, “add enumerations to the language”, “implement visibility modifiers (private, default, public)”, “improve and document the standard library”, finish the Eclipse plugin and the manual, and then ship “the first official stable release of the language”. Much of that list was in fact delivered. Version 0.9.0 brought enums and value dispatch; 0.9.2 (13 September 2003) unified functions and methods - “now everything is a method, which is more flexible” - and added range expressions; 0.9.5 (11 December 2003) introduced the Type param pattern syntax and made Object a supertype of every type; 0.9.6 (20 February 2004) added custom constructors, enums with fields and a C#-style using; 0.9.7 (8 March 2004) added the override keyword with covariant return types. The community was at its busiest: the nice-info list received 207 messages in February 2004, the nice-commit list carried thousands of CVS notifications, an IRC channel had opened on Freenode on 15 June 2003, an Eclipse plugin tracked each release from 0.9.3 onward, and Bonniot’s own Debian package was accepted into the archive on 20 March 2004.

Version 0.9.10 (25 February 2005) marked a milestone of a different kind: “an important part of the Nice compiler has been converted from Java to Nice itself! A lot of thanks go to Luc Perrin and Arjan Boeijink for this achievement.” The nice-commit list had its busiest month, 232 messages, that March. Then the pace slowed. Version 0.9.11 (7 June 2005) was a large release - maybeNull, nullness analysis of assignments, Java 5 wildcards, binary literals, forced assertions - but 0.9.12 did not follow until 25 January 2006, and 0.9.13, dated 25 November 2007 and published on SourceForge on 19 December, was the last. Its headline change was support for the “covariant return types” bridge methods that Java 5 and Java 6 compilers generate - a sign that the JVM ecosystem was moving on and Nice was adapting rather than leading.

Design philosophy

Nice’s manual opens with two epigraphs - Alan Perlis’s “a language that doesn’t affect the way you think about programming, is not worth knowing” and Whitehead’s “the art of progress is to preserve order amid change and to preserve change amid order” - and then states its three pillars:

  • Safety. “Nice detects more errors during compilation than existing object-oriented languages (null pointer accesses, cast exceptions).” Bonniot’s separate safety article argues that “programming languages should rule out as many trivial errors from programs as possible”, that ML and Haskell already do so but “enforce a quite different programming style”, and that Nice brings the same guarantees to a Java-shaped language.
  • Modularity. “In object-oriented languages, it is possible to add a new class to an existing class hierarchy. In Nice, it is also possible to add methods to existing classes without modifying their source file. This is a special case of multi-methods.” Because methods live in packages rather than classes, a library can be split into packages “dealing with different aspects”, and “development can then happen independently of each package”.
  • Expressivity. “Many repetitive programming tasks can be avoided by using Nice’s advanced features. Ever got bored of writing tons of loops, casts, overloaded methods with default values, anonymous classes, …?”

Underneath these is a firm commitment to pragmatic research: the language was to “put into practice state-of-the-art results from academic research” while remaining, in the tutorial’s words, presentable “as an extension of the Java programming language”. Nice programs use Java libraries directly, subclass Java classes, and produce jars that Java programs can call; the compiler is “itself written in Java and in Nice” and “needs a JVM version 1.2 or higher to run”. The site’s Presentation page adds that Nice programs “can be compiled to native programs with a native Java compiler”, and the source tree ships a nicec.gcj wrapper for that purpose.

Key features

Multi-methods and open classes

The central idea is that a method declaration - a name, a return type and a parameter list - is separate from its implementations, and that implementations can be written anywhere, in any package, and can specialise on the run-time class of any argument. The manual’s example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Vehicle {}
class Motorcycle extends Vehicle {}
class Car extends Vehicle {}

// Declare method
int numberOfWheels(Vehicle vehicle);

// Default implementation
numberOfWheels(vehicle)
{
  throw new Exception("Unknown number of wheels!");
}

// Specialize for Cars
numberOfWheels(Car car)
{
  return 4;
}

// Specialize for Motorcycles
numberOfWheels(Motorcycle mc) = 2;

Dispatch considers every argument, not just the receiver. The tutorial’s equals example shows why that matters: in Java, Person.equals(Object) needs an instanceof test and a cast; in Nice, equals(Person p1, Person p2) = p1.name.equals(p2.name) && p1.age == p2.age; is selected only when both arguments are Persons, “so the type of the second argument is also known, and no manual instanceof and no cast are necessary”. A #Type name pattern matches the exact class rather than any subclass. The compiler checks that the set of implementations covers every combination of argument types the declaration allows and reports ambiguities, and Bonniot’s “Visitor pattern considered useless” article argues that the whole Visitor machinery is “a trick to introduce multiple dispatch in a language that lacks it”.

Because implementations may be written outside the declaring package - “even in java.*” - Nice classes are open: a new method on java.lang.String is one line in your own package, with no wrapper class.

Value dispatch

From 0.7.7 onward, implementations could also be chosen by literal value - integers, characters, booleans, strings, enums, null, and package-level constants that hold unique objects - and, from 0.9.0, by integer comparison:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int abs(int n);
abs(n>=0) = n;
abs(n<0) = -n;

String booleanToYesNo(boolean bool);
booleanToYesNo(true) = "yes";
booleanToYesNo(false) = "no";

<T> int containsHowMany(?List<T> list);
<T> containsHowMany(List list) = list.size();
containsHowMany(null) = 0;

The manual notes that this “can be more flexible than switch or if/else statements, because you can always add new alternatives conveniently by just adding a new method implementation”; for abs the compiler “knows that method abs is covered for all int values”.

Option types and nullness inference

Nice distinguishes String, which cannot hold null, from ?String, which can. “It is not possible to pass a ?String where a String is expected, unless the compiler can prove it’s not null”, and the proof is an ordinary test:

1
2
3
4
5
6
7
8
9
void foo(String arg) {...}

void bar(?String arg) {
  if (arg != null) {
    // Here Nice knows arg is not null
    foo(arg);
  }
  foo(arg); // compilation error: arg may be null
}

Nullness inference applies to local variables only - deliberately, because a field “may have changed (updated by another thread, perhaps) between the time the value was checked for nullness and the time it is actually used” - so the idiom is to copy a field into a let first. Type parameters can be marked !T (non-null) or ?T, arrays can be String[?], the || operator returns its left operand if non-null and otherwise its right, and notNull(e) asserts non-nullness with a runtime check when assertions are enabled. Java methods are assumed to return possibly-null values unless retyped: String getName(MyJavaClass) = native String MyJavaClass.getName(); tells the compiler once and for all that the result is never null, and 0.9.7 added import some.java.pkg.* (!); to declare a whole package null-hostile.

Parametric classes, abstract interfaces and precise types

Classes and interfaces take type parameters (class Stack<T> { List<T> contents; }), and primitives are boxed and unboxed automatically in polymorphic code. The type language is richer than Java’s: constraints such as <Collection C, T> C<T> filter(C<T>, T->boolean) say that filter returns the same kind of collection it was given, the standard library’s <T, U | U <: T> U[] fill(T[] array, int->U value) relates two parameters, and the keyword alike abbreviates “the same type as the receiver”, so that alike copy(); in class Foo gives x.copy() the type of x with no cast - the tutorial’s point being that alike needs “no special treatment … in the core type system”, being sugar for polymorphic constrained types.

Abstract interfaces, the feature Bonniot’s kinds paper justified, are interfaces that a class can be declared to implement after the fact - “even the source code is unnecessary”. The manual’s logging example makes java.lang.String, nice.lang.Throwable and javax.servlet.http.HttpServletRequest all implement a LogEntry abstract interface in the user’s own package:

1
2
3
4
5
6
7
8
9
abstract interface LogEntry
{
  String toLogString();
  int severity();
}

class java.lang.String implements LogEntry;
toLogString(String s) = s;
severity(String s) = DEBUG;

A method typed <LogEntry E> void log(E entry, int severity = -1) then accepts strings, exceptions and requests but rejects log(5) at compile time, “because byte doesn’t implement LogEntry”.

Functions, tuples, named and optional parameters

Anonymous functions are first-class values - strings.foreach(String s => buff.append(s));, [1,2,3,4,5].map(int num => String.valueOf(num)), strings.foldLeft((String accum, String s) => accum + s, "") - with inferred return types, and functional types are written with arrows. Tuples (String, int, String) let a method return several values and allow (x, y) = (y, x). Parameters can be named at the call site, in any order, and given defaults that may refer to earlier parameters:

1
2
3
4
<T> T[] slice(T[] array, int from = 0, int to = array.length - 1);

copy(from: f1, to: f2);
copy(to: f3, from: f4);

Constructors are generated automatically from a class’s fields and are called with named arguments - new Car(brand: "Renault", model: "Cinq") - which the manual defends at length: reordering constructor parameters in Java “requires modifying all the call sites, which is at best tedious and error-prone”.

Contracts, assertions and everything else

Contracts attach to method declarations with requires and ensures, and result names the return value in a postcondition:

1
2
3
4
5
6
7
8
9
interface Buffer<Elem>
{
  int size();
  boolean isFull();
  boolean isEmpty() ensures result == (size() == 0);
  void add(Elem element)
    requires !isFull() : "buffer must not be full"
    ensures  !isEmpty() : "buffer must not be empty";
}

Checking is off by default, enabled with java -ea on JDK 1.4 and later, and - unlike Java’s own assertions - available “on any version of the JVM, even prior to 1.4”. Beyond that, the language accumulated a long tail of conveniences: let and var locals with inferred types, for (elem : array) loops (from April 2003, ahead of Java 5), ranges 1..10, multi-line """ strings, string juxtaposition "x has value "x, list slicing with @ for the last index, multiple comparisons 0 < i < N, enums with fields, a using statement, override with covariant return types, class initializers, and local methods.

The toolchain

The compiler nicec takes a package as its unit of compilation - “each package resides in a separate directory” - finds and recompiles imported packages automatically, “enjoys separate compilation”, and with -a program.jar produces a self-contained jar. Alongside it the source tree provides nicedoc, the niceunit test runner, Ant tasks for all three, an Emacs mode, a Maven plugin descriptor, and a regression testsuite driven from .nice fixture files. Bytecode generation uses Per Bothner’s gnu.bytecode library from Kawa; parsing uses JavaCC. The runtime classes are licensed “GPL + linking exception”, so “libraries and programs written in Nice can be licensed under any terms”, while the compiler is GPL.

Evolution

Read end to end, the release notes tell a story of a language converging on Java’s future while trying to stay ahead of it. Parametric classes were in the very first tutorial, and support for Java’s own GJ-style generic types followed in October 2002; the enhanced for arrived in April 2003 and enums in July 2003, each ahead of the corresponding Java 5 feature, while static imports in January 2006 trailed Java 5’s by more than a year - and Nice’s versions were usually more general (enums with fields and interfaces, dispatch on enum values, for with an inferred element type). At the same time the syntax drifted steadily toward Java: the original ML-flavoured fun(int)(int) became int->int, main(args) became void main(String[] args), the @Type pattern became Type param, the fun keyword was made optional and deprecated, Any was deprecated, and the distinction between functions and methods was abolished.

The other trend was self-hosting. The first tarballs were Java programs; by 0.9.10 “an important part of the Nice compiler” was Nice, and the 0.9.13 tree contains 111 .nice files in src/ alongside 332 .java files, with the entire standard library, the documentation generator, the unit-test tool and the visibility checker in Nice. Muschevici’s 2009 measurement of about 35,000 lines of Java and 23,000 lines of Nice is a fair snapshot of where that process stopped.

Current relevance

Nice is dormant. The final release is 0.9.13 from late 2007; the nice-commit list carried three messages in February 2008 and a single one in April 2011; nice-devel ended in September 2009 and nice-info in January 2012; Debian removed the package on 2 December 2011; the SourceForge project’s last update is dated May 2013; and in July 2020 the English Wikipedia article was deleted for lack of independent coverage. The website, manual, tutorial and downloads are all still served from nice.sourceforge.net, and the 0.9.13 source tarball builds with make from its bundled bootstrap compiler, so the language remains runnable on a modern JVM in the way this site cares about - but nobody is developing it.

Its ideas, by contrast, are everywhere. Kotlin’s String?, Swift optionals, Ceylon’s and Dart’s null safety and C# 8’s nullable reference types all draw the distinction Nice was already drawing by the start of 2002 between a reference that may be null and one that may not, and enforce it with the same flow-sensitive checks on locals. Kotlin’s extension functions and Scala’s implicit classes reproduce the modularity half of open classes; Java’s own records, var locals, enhanced for, switch on strings and pattern matching cover, one at a time, the named-field constructors, local inference, for (x : xs), string dispatch and typed instanceof refinement that Nice had between 2002 and 2004. None of those languages cites Nice, and this page does not claim they borrowed from it; the point is that Nice was an early, working, complete demonstration that a Java-shaped language could have them all at once.

The one place Nice retains a citation trail is the literature on multiple dispatch, where it serves as the JVM data point. Muschevici, Potanin, Tempero and Noble’s OOPSLA 2008 study measured how much real programs in six multi-method languages actually dispatch on more than one argument; the Nice compiler contributed 1,184 generic functions and 1,615 concrete methods, and the finding that Nice and MultiJava - the two languages that also offer Java-style static overloading - used multiple dispatch in only 1.0-1.4% of generic functions, against about 4% across the corpus, was offered by the authors as evidence that “a language specification does not dictate a programming style”.

Why it matters

Nice is worth remembering for three reasons. First, it was a genuine bridge between type theory and practice: a POPL type system and two workshop papers became a compiler that ordinary Java programmers could download as an RPM, a .deb or an Eclipse plugin, and use against the real java.util and javax.servlet libraries. Second, it got the combination right very early - non-nullable types, local inference, generics, closures, contracts and multi-methods on the JVM in 2002-2003, packaged behind Java syntax rather than ML’s - and its release notes read today like a specification for the decade of JVM-language design that followed. Third, it is an honest record of how research languages end: not with a failure but with a roadmap whose items were mostly delivered, a version number that stopped at 0.9.13, and a maintainer who moved on while the mailing lists slowly went quiet.

For the code archaeologist, the tarball is the artefact: a compiler whose packages still carry the name of the language it grew out of, a NEWS file that documents every design decision from December 2001 to November 2007 with a timestamp, and a manual whose authors could write, without qualification, “Daniel Bonniot, Nice’s creator, invented abstract interfaces.”

Timeline

1997
François Bourdoncle and Stephan Merz publish 'Type checking higher-order polymorphic multi-methods' at POPL '97, the paper that defines the ML-Sub type system - the site's research page later calls it 'the core type system' of Nice and notes that it 'proves its safety property'
2000
Daniel Bonniot registers the project 'nice' on SourceForge on 12 October and uploads the first downloadable compiler, Nice.tar, on 26 October; a second snapshot follows on 1 December. The earliest web page describes Nice as 'a research programming language' that 'demonstrates how the powerful ML-Sub type system can be used in practice', points to Bonniot's DEA graduation report for the rationale, and admits 'there is no real manual for the moment'
2001
Date-stamped snapshots (2001.04.06, 05.16, 06.06, 07.31, 10.31, 11.07, 11.23) are released through the year, at first as tarballs and briefly as jars. On 18 December the entry that the release-notes file calls the 'Initial Release' (0.0.011219) appears, now also packaged as a Debian .deb
2002
Bonniot presents 'Type-checking multi-methods in ML (a modular approach)' at FOOL 9, the Ninth International Workshop on Foundations of Object-Oriented Languages, in Portland on 19 January, and 'Using kinds to type partially-polymorphic methods' - 'the theoretical basis for abstract interfaces in Nice' - at the TIP'02 Workshop on Types in Programming (published in ENTCS volume 75 in February 2003). The compiler gains nullness inference (2 January), overriding of Java methods and break/continue/do (5 February), and Java-style reachability and definite-assignment checks that let 'more than half of the Jacks (java compiler testsuite) tests' pass (5 April)
2002
Version 0.5 (18 April) adopts the arrow notation int->int for functional types, makes assigning a possibly-null value to a non-null variable an error instead of a warning and makes the compiler compatible with JDK 1.4; 0.5.1 (29 April) introduces the automatic constructor that takes field values as named arguments; 0.5.4 (13 June) adds the short anonymous-function syntax 'int i => i+1' and renames iter to foreach; 0.6 (11 July) is the 'first stable version'; the nice-devel mailing list opens in June
2002
The 0.7 development series begins on 16 July with super calls and transient/volatile fields; 0.7.2 (23 August) lets existing classes be declared to implement new abstract interfaces; 0.7.4 (1 October) adds support for Generic Java types so that java.util collections can be used as List<String>; 0.7.5 (20 November) adds design by contract - assertions, pre- and post-conditions - and the || operator on optional values
2003
0.7.6 (21 January) adds multiple comparisons 'if (0 < i < N)' contributed by Arjan Boeijink; 0.7.7 (5 March) adds dispatch on boolean, integer and character literals; 0.7.8 (8 April) implements the enhanced for loop 'similar to java 1.5 proposal'; 0.7.9 (19 April) lets implementations of Java-declared methods such as equals dispatch on all their arguments and introduces let/var local declarations with inferred types; the nice-commit list starts relaying CVS changes in February
2003
Version 0.8, the second stable release, ships on 14 May (SourceForge folder dated 29 May) with BigInteger operators, and the roadmap describes 'Java 1.5-compatible generic classes', contracts and dispatch on boolean and integer values as its highlights; the #nice IRC channel on Freenode opens on 15 June
2003
0.9.0 (release notes dated 18 July; SourceForge folder 17 July) adds value dispatch on integer comparisons, string literals and global constants, simple enums, tuple-pattern parameters and library archives; 0.9.1 (9 August) adds class initializers and covariant overriding of final fields; 0.9.2 (13 September) removes the separate concept of 'function' so that 'everything is a method' and adds range expressions 'for (i : 1..10)'; 0.9.3 (18 October) adds triple-quoted multi-line strings; 0.9.5 (11 December) introduces the 'Type param' pattern syntax and makes Object a super-type of every type. The Nice user's manual by Bonniot, Bryn Keller and Francis Barber carries a 2003 copyright, and the first Eclipse plugin, net.sf.nice 0.9.3, appears in November
2004
0.9.6 (20 February) adds custom constructors, enums with fields, the block syntax 'loop(5) { ... }' and a C#-style using statement; 0.9.7 (8 March) adds the override keyword with covariant return types and deprecates 'Any'. The nice-info list peaks at 207 messages in February; Nice enters the Debian archive when 0.9.6-1, packaged by Bonniot himself, is accepted on 20 March; 0.9.8 (29 June) compiles Nice methods inside classes 'whenever possible' to ease use from Java tools, and its Eclipse plugin (24 June) supports Eclipse 2.1 and 3.0
2004
Ricky81's French-language 'Présentation du langage NICE' is published on developpez.com on 25 May; Alexandre Frey, whose author tags appear throughout the mlsub typing engine, defends his thesis 'Approche algébrique du typage d'un langage à la ML avec objets, sous-typage et multi-méthodes' on 18 June; 0.9.9 (22 September) uses 'assert o instanceof String' to refine types, parametrises java.lang.Class<T>, and adds list slicing with '@' for the last index
2005
0.9.10 (25 February) announces that 'an important part of the Nice compiler has been converted from Java to Nice itself', crediting Luc Perrin and Arjan Boeijink, and lets imported Java methods be generalised so that java.util.Collection can satisfy a Nice abstract interface; the nice-commit list records its busiest month, 232 messages, in March. 0.9.11 (7 June) adds maybeNull, nullness analysis of assignments, instanceof on option types, Java 5-style unbounded wildcards, binary literals and forced '!assert' assertions. Bonniot's doctoral thesis 'Typage modulaire des multi-méthodes', directed by Didier Rémy, is recorded by theses.fr as defended in 2005
2006
0.9.12 (25 January) adds static import ('import static java.lang.Math.PI') and source-encoding options, and its release notes claim 'improved compilation speed (about 15%)' - a figure the notes give without any published benchmark; it is uploaded to Debian on 26 March and migrates to testing in April and again in October after a summer removal
2007
0.9.13, the final release, is dated 25 November in the release notes, uploaded to Debian on 3 December and published on SourceForge on 19 December: it supports the 'covariant return types' bridge methods generated by Java 5 and Java 6 compilers, updates the Emacs mode for Emacs 22 and fixes method generalisation with primitive types; nice-devel has its last busy month (52 messages) in December
2008
The nice-commit list falls silent after three messages in February (a single further message arrives in April 2011). In October, Radu Muschevici, Alex Potanin, Ewan Tempero and James Noble publish 'Multiple dispatch in practice' at OOPSLA 2008, an empirical study of six multiple-dispatch languages whose corpus includes the Nice 0.9.13 compiler
2009
Niels Thykier's non-maintainer uploads 0.9.13-3.1 and 0.9.13-3.2 (21-22 September) keep the Debian package building; the last nice-devel message is posted in September. Muschevici's Victoria University of Wellington MSc thesis expands the OOPSLA study, describing Nice as developed 'as part of an academic project on object-orientation' at INRIA Rocquencourt and noting that, at 0.9.13, 'it is not considered feature complete'
2011
Debian removes the nice package from unstable on 2 December (bug #647821) and from testing the next day, ending the language's presence in a mainstream distribution; the last message on the nice-info list follows in January 2012
2020
The English Wikipedia article 'Nice (programming language)' is deleted on 27 July after a deletion discussion that found 'nothing but casual mentions and namedrops' in independent sources; the SourceForge site, the manual and the downloads remain online

Notable Uses & Legacy

The Nice compiler (nicec) and its toolchain

Nice's largest program is its own compiler. The 0.9.13 source tree mixes the original Java front end (packages bossa.syntax, bossa.modules, bossa.parser), the mlsub constraint-solving type checker and Per Bothner's gnu.bytecode with a growing body of Nice code - nice.tools.compiler, nicedoc, niceunit, the Ant task, the visibility checker and the whole standard library (nice.lang, nice.functional, nice.getopt, nice.io) are written in Nice, and 0.9.10 announced that 'an important part of the Nice compiler has been converted from Java to Nice itself'. Muschevici's 2009 study measured roughly 35,000 lines of Java and 23,000 lines of Nice in the project

Empirical research on multiple dispatch (OOPSLA 2008)

Muschevici, Potanin, Tempero and Noble's 'Multiple dispatch in practice' used the Nice 0.9.13 compiler as its Nice corpus alongside SBCL and McCLIM (CLOS), Open Dylan and Gwydion (Dylan), Vortex (Cecil), Whirlwind (Diesel) and the MultiJava Location Stack. The native Nice code contributed 1,184 generic functions and 1,615 concrete methods; the study found that Nice and MultiJava, which offer Java-style static overloading alongside multi-methods, used multiple dispatch in only 1.0-1.4% of generic functions, against about 4% across the whole corpus, and cited the Nice compiler as 'a prime example' that language support does not dictate programming style

Debian GNU/Linux packaging (2004-2011)

Bonniot maintained an official Debian source package 'nice' - an architecture-independent jar plus wrapper scripts - from 0.9.6-1 (accepted 20 March 2004) through 0.9.13-3 (December 2007); it migrated into testing repeatedly, received two non-maintainer uploads from Niels Thykier in September 2009, and was removed from unstable on 2 December 2011. The install page also offered RPMs for Red Hat and Mandrake, a Windows zip, a Unix tarball and a source distribution with a bootstrap compiler

Eclipse plugin net.sf.nice

A dedicated Eclipse plugin, with its own nice-eclipse mailing list, bundled the compiler so that 'you don't need to install it separately'; releases 0.9.3 (November 2003), 0.9.6, 0.9.7 and 0.9.8 (June 2004) tracked the compiler, and the 0.9.7 release notes record it working with Eclipse 3.0 M7 while 2.1 remained 'the preferred version'. An Emacs nice-mode was already documented on the 2000-era install page and still ships in the 0.9.13 source tree

Doctoral research at INRIA and the École des Mines

Nice was the implementation vehicle for Bonniot's 2005 thesis 'Typage modulaire des multi-méthodes', whose abstract closes with 'an overview of the implementation of all those ideas in a complete language, Nice', and it put the ML-Sub system of Bourdoncle and Merz - itself developed at the École des Mines de Paris - to practical use; the mlsub engine in the compiler carries the author tags of Alexandre Frey, whose 2004 thesis gave an algebraic account of typing an ML-like language with objects, subtyping and multi-methods

Language Influence

Influenced By

Java ML GJ (Generic Java) Lisp C#

Running Today

Run examples using the official Docker image:

docker pull
Last updated: