This file describes differences between Clean 1.3.x and Clean 2.x. It is aimed to help you to port Clean 1.3.x programs to Clean 2.x First of all: Clean 2.x is not downward compatible with Clean 1.3.x. Probably you have to change your 1.3.x sources to get them through the Clean 2.x compiler. To be able to write Clean programs that can be compiled with both compiler versions we have built a simple preprocessor into the Clean 2.x compiler. This feature allows you to exclude certain parts of your source, depending on which compiler version you use. See section "The Preprocessor" for details. The following sections describe the differences in detail. New Features in Clean 2.0 ------------------------- Clean 2.x has dynamic types, multiparameter type classes and generic classes. For these items see the Clean language report. With Clean 2.x cyclic dependencies between dcl modules are allowed. Differences in Expression Syntax -------------------------------- There is no strict let-before expression (let!) anymore in Clean 2.x. You still can enforce strict evaluation using the strict hash let (#!). Differences in the Type System ------------------------------ 1. Array handling has become different, see section "Differences in the Standard environment 2. There is a difference in resolving overloading. Consider the following code: class c a :: a -> a instance c [Int] where c [1] = [2] f [x:xs] = c xs Although this is accepted by Clean 1.3.x, Clean 2.x will complain Overloading error [...,..,f]: c no instance available of type [a] The Clean 2.x compiler applies no type unification after resolving overloading. So c is in function f applied to a list with a polymorph element type ([a]). And this is considered to be different from the instance type [Int]. If you give f the type [Int] -> [Int] the upper code will be accepted. 3. Clean 2.x handles uniqueness attributes in type synonyms different than Clean 1.3.x. Consider the following definitions: :: ListList a :== [[a]] f :: *(ListList *{Int}) -> *{Int} f [[a]] = { a & [0]=0 } What does the ListList type synonym stand for in the type of f? In Clean 1.3.x the ListList type synonym was expanded to f :: *[*[*{Int}]] -> *{Int} whereas Clean 2.x expands it to f :: *[[*{Int}]] -> *{Int} This yields a uniqueness error in Clean 2.x because the inner list is shared but contains a unique object. Clean 1.3.x accepts the upper code. This problem happens only with type synonyms that have attributes "inbetween". What does this mean? An "inbetween" attribute is neither the "root" attribute nor the attribute of an actual argument. E.g. with the upper type synonym, the formal argument "a" is substituted with *{Int}. Note that also the "*" is substituted for "a". Further the whole result of expanding the type synonym gets the "root" attribute. Because we wrote *(ListList ...) the root attribute is "*". So far the result of expanding *(ListList *{Int}) is *[u:[*{Int]]. "u" is an attribute "inbetween" because it is neither the root attribute nor the attribute of a formal argument. Such attributes are made _non_unique_ in Clean 2.x and this is why the upper code is not accepted. The code will be accepted if you redefine ListList to :: ListList a :== [*[a]] 4. The String type has become a basic type. As a consequence you cannot import this type explicitly: from StdString import :: String is not valid. 5. The Clean 1.3.x compiler has chosen lazy arrays whenever there was a choice. The Clean 2.x compiler will complain in these cases that internal overloading could not be solved. E.g. Start = size {1} is accepted by Clean 1.3.x but not by Clean 2.x. 6. Some bugs in the uniqueness typing system were fixed. Differences in the Module System -------------------------------- The syntax and semantics of explicit import statements has been completely revised. With Clean 2.x it is possible to discriminate the different namespaces in import statements. In Clean 1.3.x the following statement from m import F could have caused the import of a function F together with a type F and a class F with all its instances from m. The syntax of Clean 2.x import statements is given below in the style that is also used in the Clean language report: ExplicitImportDef = "from" ModuleName "import" {Imports}-list ";" Imports = FunctionName | "::" TypeName [ConstructorsOrFields] | "class" ClassName [Members] | "instance" ClassName {TypeName}+ ConstructorsOrFields = "(" ".." ")" | "{" ".." "}" | "(" {ConstructorName}-list ")" | "{" {FieldName}-list "}" Members = "(" ".." ")" | "(" {MemberName}-list ")" Explanation: [notion] means that the presence of notion is optional {notion}+ means that notion occurs at least once {notion}-list means one or more occurrences of notion separated by commas Terminals are enclosed in apostrophes. All missing nonterminals are simply identifiers. For example the following import statement from m import F, :: T1, :: T2(..), :: T3(C1, C2), :: T4{..}, :: T5{field1, field2}, class C1, class C2(..), class C3(mem1, mem2) causes the following declarations to be imported: the function or macro F, the type T1, the algebraic type T2 with all it's constructors that are exported by m, the algebraic type T3 with it's constructors C1 and C2, the record type T4 with all it's fields that are exported by m, the record type T5 with it's fields field1 and field2, the class C1, the class C2 with all it's members that are exported by m, the class C3 with it's members mem1 and mem2. There is a tool called "coclPort" that is able to automatically convert Clean sources with 1.3.x import syntax to sources with 2.x syntax. Hint: The appearance of the compiler error message "... not exported as a function/macro by the specified module" is not a compiler bug. You probably forgot the "::" before a type identifier Differences in the standard environment (StdEnv) ------------------------------------------------ *&^ HOI ALLEMAAL, DEZE WIJZIGINGEN IN HET STDENV ZIJN NOG NIET GEDAAN *&@#@ *&^ THESE CHANGES IN THE STDENV ARE PLANNED BUT NOT DONE *&@#@ 1. We removed some definitions from the StdEnv. instance toString [a] // in StdList instance < (a,b), instance < (a,b,c) // in StdTuple instance == (a,b), instance == (a,b,c) // in StdTuple We defined a module port.dcl/icl. This module contains these definitions. 2. We changed the behaviour of the following functions: take in StdList Clean 1.3.x: i<0 => take i x==x Clean 2.x : i<0 => take i x==[] instance mod Int in StdInt In Clean 2.0: sign m==sign (n mod m) This keeps the invariant n == m*(n div m) + (n mod m) where div is division truncated to minus infinity This is the behaviour people expect from a mod operator. In Clean 1.3 the following invariant was kept: n == m*(n/m) + (n mod m) where "/" truncates to 0. Note that a difference only appears if exactly one argument is negative 3. We changed the argument order of the functions (s)freads, (s)fseek, freopen in module StdFile such that the File argument is the last argument. 4. We fixed some bugs: - the createArray instances in module StdArray don't crash anymore for negative size arguments. - gcd's results are now correct for the case that the result is in the range of Int. In Clean 1.3.x it was not correct in cases when one argument was minInt. 5. Array handling has become different. In Clean 2.x the Array class is a multiparameter class, whose first argument type is the array and whose second argument type is the array element. Therefore the following classes have vanished: ArrayElem, _uselectf, _uselectn, _uselectl, _updatei, _createArrayc,select_u uselect_u, uselectf_u, uselectn_u, uselectl_u, updatei_u, size_u, usize_u update_u, createArray_u, createArrayc_u, replace_u Example: Assume a function "f" that does something with arrays that had the following type in Clean 1.3: f :: (a b) -> b | select_u b & Array .a "(a b)" stands for "an array (can be strict, lazy or unboxed) with element b". In Clean 2.x this simply becomes: f :: (a b) -> b | Array a b Miscellaneous Differences ------------------------- 1. For multiparameter type classes a small change in the syntax for instance definitions was necessary. In Clean 1.3.x it was assumed that every instance definition only has one type argument. So in the following 1.3.x instance definition instance c T1 T2 the type "(T1 T2)" was meant (the type T1 with the argument T2). If this was the intention then this should be written in Clean 2.x as instance c (T1 T2) otherwise T1 and T2 will be interpreted as two types. 2. Anonymous uniqueness attributes in type contexts are not allowed in Clean 2.x. So in the following function type f :: a | myClass .a simply remove the point. 3. There are differences in some libraries. See the library's documentation. The Preprocessor ---------------- We think that for a while it should be made possible to let every source code file be compiled with both the 1.3 and the 2.0 compiler. How can this be achieved when the 2.0 compiler is not fully downward compatible? With a preprocessor of course. The simple preprocessor that we have built into into the 2.0 compiler has to distinguish parts of code that are either ignored by the 1.3 compiler or by the 2.0 compiler. To illustrate this we show how you could use the preprocessor to cope with the principial incompatibilities in conjunction with arrays. Let's suppose you have a function defined like this g a i = a.[i] and you want to write down the type for that function. This function has really different principial types in Clean 1.3.x and Clean 2.x. Your code is still compilable with both compiler versions if you write the following: //1.3 g :: u:(a v:b) .Int -> v:b | select_u b & Array .a, [u <= v] //3.1 /*2.0 g :: .(a u:b) v:Int -> u:b | Array a b, [v <= u] 0.2*/ g a i = a.[i] For the 2.0 compiler this would be the same as g :: .(a u:b) v:Int -> u:b | Array a b, [v <= u] g a i = a.[i] because the preprocessor will take care that everything between "//1.3" and "//3.1" will be ignored and that everything between "/*2.0" and "0.2*/" will _not_ be treated as a comment. For the 1.3 this would be the same as g :: u:(a v:b) .Int -> v:b | select_u b & Array .a, [u <= v] g a i = a.[i] Note that the "//1.3", "//3.1", "/*2.0" and "0.2*/" brackets have to be the first characters of the line (no preceeding whitespace characters are allowed). Otherwise they will be ignored. Furtheron such sections should neither be nested nor overlapping. Finally we have written an application "rmPreprop" that can be used to remove all these preprocessor directives when you don't want them anymore. We will remove the preprocessor in the future. Regard it as a temporary feature.