diff options
-rw-r--r-- | portToNewSyntax/1.3-and-2.0-differences.txt | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/portToNewSyntax/1.3-and-2.0-differences.txt b/portToNewSyntax/1.3-and-2.0-differences.txt new file mode 100644 index 0000000..85b338b --- /dev/null +++ b/portToNewSyntax/1.3-and-2.0-differences.txt @@ -0,0 +1,311 @@ +This file describes differences between Clean 1.3.x and 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 we 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. + 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. + 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. + There was a bug in the uniqueness typing system: Records or data constructors + could have existentially quantified variables, whose uniqueness attribute + did _not_ propagate. So it was possible to write + + f :: *{Int} -> Int + f a + #! a0 = a.[0] + a = { a & [0] = a0+1 } + = a.[0] + + :: T = E..x: C (x->Int) x + + getInt (C f x) = f x + + Start + # myObject = C f {0} + = (getInt myObject, getInt myObject) + + which resulted in (1,2), which isn't that much referential transparent. This + bug has been solved in Clean 2.x. + +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 + + +Miscellaneous Differences +------------------------- + +1. + 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. + +2. + 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 shouldn't +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 feature in the future. Regard it as +a temporary feature. |