Commit 9ad33058 authored by Martin Wierich's avatar Martin Wierich
Browse files

documentation about how to port 1.3 programs to 2.0 including differences

between Clean 1.3 and Clean 2.0
parent 1abbcfb4
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.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment