Changes from Haskell 1.2 to Haskell 1.3

There will be some incompatibilities with Haskell 1.2. These should not be serious and implementors are encouraged to provide a Haskell 1.2 compatibility mode. In brief, the following incompatible changes are being made:

Many function have been moved from the Prelude into libraries - programs may need more import statements.

The I/O system has been completely replaced. Most Haskell systems have already adopted the new I/O system so this may affect programs.

The Text class has been replaced by Read and Show.

A number of small changes in the module system: in import / export lists, instead of Mod.. the new syntax is module Mod . Synonyms are now exported without the (..) . Renaming is no longer part of Haskell. Interface files are no longer formally a part of the language.

the new syntax is . Synonyms are now exported without the . Renaming is no longer part of Haskell. Interface files are no longer formally a part of the language. Some small syntax changes: ~ and - no longer terminate an operator.

A few prelude functions have less general signatures.

ord and char have been replaced by fromEnum and toEnum.

n+k patterns have a slightly different syntax. Their use is discouraged.

Strictness annotations (a formally unofficial part of the language) are now supported but using a syntax different from existing strictness annotation schemes.

Overview

Change in the Text class

Text

Read

Show

Text

Standard Libraries

An initial Haskell library report is available at . Constructor Classes We have observed that many programmers use Gofer instead of Haskell to use Gofer's constructor classes. Since constructor classes are well understood, widely used, and easily implemented we have added these to Haskell. Briefly, constructor classes remove the restriction that types be `first order'. That is, ` T a ' is a valid Haskell type, but ` t a ' is not since ` t ' is a type variable. Constructor classes increase the power of the class system. For example, this class definition uses constructor classes:

class Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a

instance Monad [] where f >>= g = concat (map g f) return x = [x]

Constructor classes require an extra level of type information called `kinds'. Before type inference, the compiler must perform kind inference to compute a kinding for each type constructor. Kinds are much simpler than types and are not ordinarily noticed by the programmer.

The changes to Haskell required to support constructor classes are:

The syntax of types includes type application.

Built-in types have names: [] for lists, (->) for arrow, and (,) for tuples. Using type application, the type ` (,) a b ' is identical to ` (a,b) '.

for lists, for arrow, and for tuples. Using type application, the type ` ' is identical to ` '. Type constructors (but not type synonyms) can be partially applied.

The basic monadic operations: >> , >>= , and return , will be placed in the Monad class. All monads will share the same basic operations.

x :: C(a b) => a b

Monadic I/O

The addition of monad syntax should improve the improve the readability of programs that make heavy use of I/O. Strictness Annotations To achieve reasonable efficiency, Haskell programmers often use implementation-specific annotations to mark fields of data structures as strict. These strict components are evaluated when the structure is created instead of delayed until demanded. This avoids the extra overhead involved with delays and can result in much more compact structures. To help improve efficiency, we now supply a simple strictness annotation on the types in a data declaration. Using `!' in front of a type in a data declaration marks a structure component as strict. (The `!' symbol is not a keyword; it has no special meaning outside data declarations). Only data types may be marked as strict; using `!' in other type signatures is not allowed. Any or all of the conponents of a type may be marked as strict.

data Foo a = F Int !Bool a a

F

Int

Bool

Fully polymorphic strictness implies that values of any type may be evaluated when placed into a strict data structure. There are compelling arguments for marking operations which use polymorphic strictness. We use a special class, Eval , to explicitly designate the useage of polymorphicly strict operations. Every data type is a member of the Eval class. If we want to make a polymorphic component strict, we must add a "Eval" context to the declaration:

data Eval a => Foo a = F Int !Bool !a a

The libraries will use strictness annotations in the definition of Ratio and Complex , making these numeric types much more efficient. To avoid unnecessary propagation of the Eval class, it is a superclass of Num .

The functions ` strict ' and ` seq ' have been added to to give the user additional control over evaluation. While no strictness annotations have been proposed for functions, these primitives can be used to implement strict evaluation when needed.

seq :: Eval a => a -> b -> b strict :: Eval a => (a -> b) -> (a -> b) strict f = \x -> seq x (f x)

seq

strict

Strict data constructors accumulate all arguments before evaluating them:

data R = R !Int !Int R x y = seq x (seq y (makeR x y)) -- just to show the semantics of R y = R undefined -- Not an error

All datatypes, including functions, are (implicitly) members of the Eval class. Adding functions to the the Eval class means that all types are in the class. It is tempting then to omit the Eval class altogether and drop the Eval context from seq and strict . We chose not to do so because the context provides useful information. For example, a function of type a -> b is certain to be lazy in its first argument whereas a function of type Eval a => a -> b may be strict in its first argument.

Haskell 1.3 will not provide strictness annotations for functions. These can be constructed explicitly using seq if needed. We decided we do not yet know what the best way to handle annotations for function strictness so (at least for now) these are not ready for Haskell 1.3. Labeled Fields The individual components of data types in Haskell 1.2 are completely anonymous: the type definition only names the type and the constructors, not the fields of a constructor. It is inconvenient to select from, construct, or modify values associated with a constructor which has many components. A number of more general record structures have been proposed and implemented. After much discussion, we have decided to avoid the issues of inheritance or object oriented programming for the moment and provide a simple syntax which will allow the fields of a data type to be referred to by name.

We have decided to use the term `field labels' in the Haskell report instead `records'. The terminology used here is not consistant with the report.

Haskell 1.3 provides a special syntax for declaring field names for data types, and for the selection, construction, and update of values with named fields. There are no new types or new semantics; all new constructs have a simple translation to core Haskell. Strictness annotations can be used in the obvious way with the obvious meaning. A small change is required in the definition of import-export lists to accomodate field names.

Declarations

data List a = Cons {hd::a, tl::List a} | Nil data Tree a = Node {label::a, subtrees :: [Tree a]} data Date = Date {day, month, year :: Int} data NonEmpty a = Head {head :: a} | Cons {head :: a, tail :: NonEmpty a} -- example taken from page 82 of Simon Peyton Jones and David Lester's -- book "Implementing Functional Languages -- A Tutorial" data GMState = GMState { code :: GMCode, stack :: GMStack, heap :: GMHeap, globals :: GMGlobals, stats :: GMStats}

The braces used by records may NOT be omitted using layout.

The NonEmpty example demonstrates that the same fieldname can be used in different constructors provided both fields have the same type (modulo type synonyms and addition of parentheses). The following declarations would not be legal: data F = A {x::Int} | B {x :: Float} -- ILLEGAL data G = C {x::Int, x::Int} -- ILLEGAL It is also be illegal to use the same field name in scope from more than one type.

Construction

let list42 = Cons 42 Nil treeabc = Node 'b' [Node 'a' [], Node 'c' []] today = Date 11 10 1995 in show (list, treeabc, today)

let today = Date{day = 11, month = 10, year = 1995} other_day = Date{day = 10, month = 11, year = 1995} state = GMState{ code = [], stack = emptyStack, heap = emptyHeap, globals = preludeGlobals, stats = initStats} in show (today, other_day, state)

Syntacticly, the braces in records bind more tightly than any other operator, including application. Thus, f C {x = 1} is parsed as f (C {x = 1}) . We omit the space before `{' to highlight the way this parses: f C{x = 1} .

Pattern Matching

len :: NonEmpty a -> Int len (Head _) = 1 len (Cons x xs) = 1 + length xs labels (Node l ns) = l : concat (map labels ns) (Date d1 m1 y1) <= (Date d2 m2 y2) = y1 < y2 || (y1 == y2 && (m1 < m2 || (m1 == m2 && d1 <= d2)))

showsPrec d (Date{day = d, month = m, year = y}) = shows d . ('/':) . shows m . ('/':) . shows y showsPrec d (GMState{code, stack, heap, globals, stats}) = ('<':) . shows code . (' ':) . shows stack . (' ':) . shows heap . (' ':) . shows globals . (' ':) . shows stats . ('>':)

field

field = var

field = field

showsPrec d (GMState {code=code, stack=stack, heap=heap, globals=globals, stats=stats}) = ('<':) . shows code . (' ':) . shows stack . (' ':) . shows heap . (' ':) . shows globals . (' ':) . shows stats . ('>':)

showsPrec d (GMState {code=c; stack=s; heap=h; globals=g; stats=s}) = ('<':) . shows c . (' ':) . shows s . (' ':) . shows h . (' ':) . shows g . (' ':) . shows s . ('>':)

Selection

data Date = Date {day, month, year :: Int}

day, month, year :: Date -> Int day (Date{day = d}) = d month (Date{month = m}) = m year (Date{year = y}) = y

Note: Since selector functions are just ordinary functions, they can get "shadowed" if you have local variables of the same name. For example, the following has a type error because a local variable "shadows" the global selector function:

daysSinceStart d@(Date {day, month, year) = day + sum [ daysInMonth m | m = [1..month-1] ] where daysInMonth m | m `elem` [9,4,6,11] = 30 daysInMonth 2 | isLeapYear (year d) = 29 -- WRONG: year is shadowed | otherwise = 28 daysInMonth _ = 31

Only these selector functions are shadowed: field names used within braces are never shadowed by ordinary variables.

Updates

putCode :: GMCode -> GMState -> GMState putCode code' (GMCode {code, stack, heap, globals, stats} = (GMCode {code = code', stack, heap, globals, stats} = putStack :: GMStack -> GMState -> GMState putStack stack' (GMCode {code, stack, heap, globals, stats} = (GMCode {code, stack = stack', heap, globals, stats} = putStats :: GMStats -> GMState -> GMState putStats stats' (GMCode with {code; stack; heap; globals; stats} = (GMCode {code, stack, heap, globals, stats = stats'} =

Using Haskell 1.3's update expressions, these could be rewritten:

putCode :: GMCode -> GMState -> GMState putCode code' s = s{code = code'} putStack :: GMStack -> GMState -> GMState putStack stack' s = s{stack = stack'} putStats :: GMStats -> GMState -> GMState putStats stats' s = s{stats = stats'}

putCode :: GMCode -> GMState -> GMState putCode code s = s{code} putStack :: GMStack -> GMState -> GMState putStack stack s = s {stack} putStats :: GMStats -> GMState -> GMState putStats stats s = s {stats}

doAdmin :: GMState -> GMState doAdmin s = putStats (statIncSteps (getStats s)) s

doAdmin :: GMState -> GMState doAdmin s = s{stats = statIncSteps (stats s)}

doAdmin :: GMState -> GMState doAdmin s@GMState{stats} = s{stats = statIncSteps stats}

Simultaneous updates are also allowed. For example, if an American had (incorrectly) entered the date "25th January 1996" as "1/25/1996", one could use the following function to fix the record:

fixUSDate :: Date -> Date fixUSDate d = d{day = month d, month = day d}

Derived instances

show (Date{day = 25, month = 1, year = 1996}) = "Date{day = 25, month = 1, year = 1996}"

Import/Export

import Prelude(Maybe)

import Prelude(Maybe(..))

import Prelude(Maybe(Just,Nothing))

import Prelude(List(Cons,head,tail,Nil))

(All the above examples work for export too - just replace "import" by "export" throughout.) Renaming Existing Types Haskell users have often needed to declare a new name for an existing type. There were two ways of doing this: declaring a synonym, as in

type Foo = Int

data Foo = Foo Int

newtype context => simple = con type [deriving classes]

newtype Foo = Foo Int deriving Show newtype Bar a = Bar [a] deriving (Show,Eq) newtype LongName = L Int

Read

Show

At first glance, it looks as though the newtype "Foo" could have been defined using normal data types and strictness annotations.

data Foo = Foo !Int deriving Show

Using strictness annotations, evaluation of the field occurs when a Foo is created. That is: case Foo undefined of Foo _ -> True = undefined

Using newtype, evaluation of the field occurs when the field is used. That is case Foo undefined of Foo _ -> True = True

newtype

~

Foo _

newtype

Monad Syntax

Monad

>>

>>=

Syntax: Expansion: exp -> do {stmt} stmt -> exp exp stmt -> exp ; stmt exp >> do stmt stmt -> let decls ; stmt let decls in do stmt stmt -> pat <- exp ; stmt <see below>

The Haskell layout rules already allow you to write:

let x = 1 y = 2 in x + y

let { x = 1; y = 2 } in x + y

do x <- getInt y <- getInt return (x + y)

do { x <- getInt; y <- getInt; return (x + y) }

As in a list comprehension, variables bound by patterns are scoped over the following statements in the do . The do has the same type as the last statement.

The translation of pat <- exp; stmt depends on the pattern pat .

The most common case is that pat is a variable in which case the translation is: exp >>= \pat -> do stmt In fact, this translation is used for any failure-free pattern such as "x", "~(x:xs)", "~(Just 3)". A pattern is failure-free when it is either irrefutable or consists of a constructor from a single-constructor data type applied to failure-free patterns. For example, do (x,y) <- getPair; return (x+y) is translated to getPair >>= \(x,y) -> return (x+y)

is a variable in which case the translation is: In fact, this translation is used for any failure-free pattern such as "x", "~(x:xs)", "~(Just 3)". A pattern is failure-free when it is either irrefutable or consists of a constructor from a single-constructor data type applied to failure-free patterns. If pat is pattern which might not match at runtime (ignoring bottom), the translation is: exp >>= \ x -> case x of { pat -> do stmt; _ -> zero } (where x is free in stmt ). For example, do Just x <- getOpt "x"; foo x is translated to getOpt >>= \ z -> case z of { Just x -> foo x; _ -> zero } Note that using this translation results in monad expressions of type MonadZero m => m a instead of Monad m => m a . The above special cases are included to reduce the clutter in IO monad expressions.

A let in a do is scoped over all remaining statements. Note there is no in ; this distinguishes this from an ordinary let.

H Haskell 1.3 omits several features from earlier proposals and the Gofer implementation. In particular, there is no support for "guards".

do x <- foo if even x bar

-- raise an error if condition fails. assert :: Monad m => Bool -> String -> m () assert p msg = if p then return () else error ("Assertion failed: " ++ msg) -- return a zero if condition fails. -- Similar to guards in list comprehensions. guard :: MonadZero m => Bool -> m () guard p = if p then return () else zero do x <- foo assert (even x) "foo didn't return an even result at line 32 of Bar.lhs" y <- bar x guard (x `elem` y) return y

The Module System

Renaming has been removed.

Qualifiers are used to handle name clashes between modules.

The closure rule has been relaxed.

Interface module are no longer a part of the language.

All names are redefinable - no PreludeCore.

The C-T rule has been relaxed. The rules for import / export of instances are different.

Qualified Names

import

Prelude.foldr

foldr

Prelude

Using qualified names prevents or resolves name clashes between different modules.

Qualified names result in more readable code since import declarations need not be consulted to find the defining module for a name.

qualified

By default, the name of the imported module is used as the qualifier in a qualified name. An import declaration may contain an as clause to specify a different qualifier.

Qualified names are defined in the lexical syntax. Thus, ` Foo.a ' and ` Foo . a ' are quite different. No whitespace is permitted in a qualified name. Symbols may also be qualified: ` Prelude.+ ' is an operator which can be used in exactly the same manner as ` + '.

Note that the ` . ' operator presents a syntactic problem. It causes ` Foo.. ', which was used in export lists (this syntax has changed) and occasionally in arithmetic sequences, to parse as a qualified name instead of a usage of the ` .. ' token. Inserting a space before the ` .. ' will resolve this problem.

Qualifiers are prohibited in definitions:

M.x = 1

module M3(M1.x,M2.x) where -- an error import qualified M1(x) import qualified M2(x)

Although as appears in the syntax for imports, it is not treated as a keyword in other contexts. Redefinable Names A complaint about Haskell has been that names defined in PreludeCore cannot be redefined in any way. Since many operators, like + , - , > , and == , are defined in PreludeCore this has made some users deeply unhappy -- this restriction prevents any sort of alternative numeric class structure which uses the standard symbols, for example. There is no real reason for stealing all these names from the user; in Haskell 1.3 PreludeCore is no longer special: all ordinary names are redefinable. Special syntax will remain attached to Prelude names; thus ` [x] ' would always refer to lists as defined in the Prelude. As before, the Prelude module is implicitly imported (in unqualified form) unless an explicit import is found. There is also an implicit (and unavoidable) qualified import of the Prelude which is used to define the meaning of various pieces of syntactic sugar. This eliminates the need to make PreludeCore symbols immutable. Since qualified names can always be used for imported entities, any Prelude entities that the programmer has chosen to shadow can still be referred to using ` Prelude. '.

Unfortunately, while Prelude names are redefinable, translations of special syntax yield fixed definitions in the Prleude. Although subtraction (-) can be redefined, negation is a special syntax and always refers to the negate defined in the Prelude. Similarly, numeric constants have an implicit fromInteger / fromRational. This implicit function is always as defined in the Prelude even when fromInteger is redefined.

C-T Rule relaxation

The visibility of instance declarations presents a problem. Unlike classes or types, instances cannot be mentioned explicitly in export lists. Instead of changing the syntax of the export list, we have adopted a simple rule: all instances are exported regardless of the export list. All imports bring in the full set of instances from the imported module. An instance is in scope when a chainof import statements leads to the module defining the instance.

Interface Files

Exporting Type Synonyms

Closure

Expanded Character Set

Other Changes to Haskell

Polymorphic Recursion

f x y = if f True False then x else y

List Comprehension Let Bindings

qual -> let {decls}

in

Standard Annotations

Minor Syntax

There is a new syntax for hex and octal constants

The types listed in a default declaration must always be in parentheses

Empty export lists are now allowed

Presymbols are gone; this allows "~" and "-" to be embedded in a symbol

Extra commas are allowed in import / export / hiding lists

\begin{code} - \end{code} is now allowed in literate files as an alternative to ">" ("Bird tracks")

Exporting entire modules uses "module M" instead of "M..". This prevents syntax errors due to qualified names.

The default module header is now module Main(main) where . This fixes unintentional monomorphism whining by the compiler.

. This fixes unintentional monomorphism whining by the compiler. The interaction of layout with explicit braces has been clarified: within explicit braces, layout established outside the braces is ignored.

Features Removed from Haskell

n+k patterns are still present, but their use is officially discouraged.

Last update: September 16th, 1998