Title: More on types
1More on types
2Soundness
- Robin Milner well-typed programs dont go
wrong! - What does that mean?
- define what it means to go wrong
- show that it cannot happen
- Semantical soundness, subject reduction
3Semantical Soundness
- if G e t then e G ? t
- in words if e type checks, then its semantic
interpretation maps values from the
interpretation of its context into the
interpretation of its type.
4Operational Soundness
- Subject reduction
- if G e t, and e ? v, then G v t
- Now, define e ? ? to mean evaluation of e can go
wrong. - Make sure G ? t never holds.
5Imperative Features
boo, hiss!!
- soundness can be affected!
- imperative in a wide sense
- mutable variables
- logical variables (assign once!)
- I/O, channel communications
- value carrying exceptions
- callcc
6The Problem (SML Syntax)
- let val ls ref
- in ls12,34
- hd(!ls)(hd(tl(!ls)))
- end
would evaluate to ? if it type-checked...
7Why should it type-check?
- ref a -gt a ref
- () a ref -gt a -gt unit
- ! a ref -gt a
- If we have unrestricted let-polymorphism,
- if these operators have these types,
- then it simply does type-check!
8Fixes
- monomorphic references only
- no heap objects
- type imperative features differently (SML90)
- restrict let-polymorphism (SML97)
- change the operational semantics of let
Warning just because somebody suggests a fix
does not mean it is going to work!
9Examples
- change the operational semantics of
- let xe in e
- reevaluate e for every occurrence of x in e
- value polymorphism only introduce ?-types when e
is a value (e.g. a function)
10Repercussions on Language Design
- SML (both 90 and 97 versions) does not always
have principal types there can be unresolved
type guesses. - in SML90
- types revealed implementation details
- the module system was affected
- unapproachable for non-expert users
11Recursive Types
- In most FP languages, recursive types are not
just recursive types, they come with baggage - they can be parametric
- they come with constructors
- they are generic, each datatype is a completely
fresh type, different from all other types
12Reasons
- historical ML of the LCF system
- convenient to use
- its what we usually want anyway
- separating out these features may be verbose
- convenient to implement
- type inference uses f.o. unification
- no cyclic term structures for representing types
13Problems with genericity
- datatypes inside expressions
- permitted at all?
- if, a fresh type for every evaluation?
- datatypes inside parametric modules
- dependent on parameters, extensionality?
- as parameters?
- open datatypes (with free type variables)
- forbidden!
14Alternative
- use fold and unfold
- unfold (rec a.t) -gt t(rec a.t)/a
- fold trec a. t/a -gt (rec a.t)
- permit cyclic structures at type level
- type inference uses unification of regular trees
- problem empty list defined as...
- fold (Left ())
15Typing of letrec
- the typing rules for let-expressions indicate
that the binding is not recursive (Hindley-Milner
system) - in SML, recursion is a separate feature
- in Haskell/Miranda/Clean all lets are
automatically recursive - Is there a problem here?
16Decidability Problem
- Type inference for polymorphic letrecs is
undecidable (known since 1992). - Haskell etc. use a certain heuristic to approach
the perfect solution, based on a programs call
graph - there are effective complete heuristics that may
fail to terminate.
17Eh?
- type inference
- walks over a term,
- uses the types of variables when encountering
them, and - applies f.o. unification when necessary
- unification is decidable isnt it?
- yes, but we may have to
- instantiate the types of variables
- and overall this gives semi-unification, not
unification
18Unification vs. Semi-Unification
- Given pairs of terms (p,q) and (r,s)...
- unification find a substitution ? such that
?(p)? ?(q) and ?(r) ?(s) - semi-unification find a substitution ? and
substitutions ? and ? such that ?(?(p))? ?(q)
and ?(?(r)) ?(s)
19Simplest Example
- strange x strange True strange 4
- ...fails to type-check in Haskell etc.
- But, it can be assigned a type
- strange a -gt b
- The problem is that the use of strange on the rhs
uses two type instances of its polymorphic type.
20Is this useful, ever?
- In the presence of recursive parametric
datatypes, yes we come to that later. - In the absence of this feature - maybe.
It is occasionally useful, but it is an open
problem whether there are any functions we cannot
write without that feature.
21Polymorphic Recursion
- if our datatype constructors change parameters
within their recursive definitions then we do
need a similar pattern of recursion for our
functions - would we ever want to write such types?
22First Example
- data Twolist a NIL
- CONS a (Twolist(a,a))
- size Twolist a -gt Integer
- size NIL 0
- size (CONS x ls) 1 2size ls
Note size runs in logarithmic time! Note values
of type Twolist a represent 2k-1 elements of type
a!
23Second Example
- data BinList a NIL CONS (Maybe a)
(BinList (a,a)) - (!) BinList a -gt Integer -gt a
- CONS Nothing ls ! n sel ls n CONS (Just a) ls
! 0 a CONS (Just a) ls ! n sel ls (n-1) - sel ls n let (a,b)ls ! (n div 2) in if n
mod 20 then a else b
24...continued
- cons a -gt BinList a -gt BinList a
- cons x NIL CONS (Just x) NIL
- cons x (CONS Nothing ls)
- CONS (Just x) ls
- cons x (CONS (Just y) ls)
- CONS Nothing (cons (x,y) ls)
on average, cons-ing an element costs 2 calls of
cons
25What can we do with this?
- various forms of balanced trees
- red-black trees, AVL trees, Braun trees
- random access lists
- even closed ?-terms (next example)
- Caution restricted patterns of recursion !
26?-terms
- data Lambda a
- Var a
- Application (Lambda a)(Lambda a)
- Abstraction (Lambda (Maybe a))
- Now, Lambda Empty give us closed terms.
27Programming with this, I
- fv Lambda a -gt a
- fv (Var x) x
- fv (Application f a) fv f fv a
- fv (Abstraction t)
- v Just v lt- fv t
28Programming with this, II
- subst Lambda a -gt (a-gtLambda b) -gt Lambda b
- subst (Var x) f f x
- subst (Application a b) f
- Application (subst a f)(subst b f)
- subst (Abstraction t) f
- Abstraction (subst t g)
- where g Nothing Nothing g (Just
v) subst (f v) (Just . Var) - The use of function arguments is quite typical
for these data types.
29Existential Types
- polymorphism gives us normally universal type
quantification - an existential type is essentially an abstract
type whose implementation details we do not know
or have not access to - existential types arise through encapsulation and
information hiding (or are a feature!)
30Existential Types... as a feature
- Example
- data Datum
- forall z. DAT z (z-gtz)(z-gtBool)
- s1 DAT 5 (1) ((0).(mod 2))
- s2 DAT True not id
- once Datum -gt Bool
- once xs g(f a) DAT a f g lt- xs
s1 and s2 have the same type! once s1,s2 is
well-defined!
31...with the class system
- class Dat z where fun z-gtz
- prd z-gtBool
- data Datum
- forall z. Dat z gt DAT z
- instance Dat Integer where ...
- once Datum-gtBool
- once xs prd(fun z) zlt-xs
32Taking it a step further
- newtype Showable forall n. Show n gt S n
- instance Show Showable where show (S n) show n
- type-dependent functions!
- problems with binary operations...
33Phantom Existentials
- data Tree a b Lf a Tree a b !
Tree a b - newtype Baltree a forall b. TREE (Tree a
b) - if we hide Lf, and replace it by
- singleton x TREE (Lf x)
- then all Baltree values are balanced trees.
34Logic and Types
- there is a close relationship between type
systems and logics - often we just take some judgement ? ?, augment
it with a proof object that records the
application of a proof rule ? ? ?,... - and bingo, weve got a typing rule
35Sub-structural Logics
- people pay now a lot more attention to the
apparently trivial rules - weakening, throwing away information
- pairing re-uses the context!
36Linear Logic
- is a logic in which you cannot (at will) throw
away a premise, or copy a context - model the situation
- if I have 5 I can buy a T-Shirt
- if I have 5 I can buy an Indian takeaway meal
- but not if I have 5 I can buy both the T-Shirt
and the takeaway meal - this also made it into P.L. design
37Linear, Affine, Unique... Types
- linear types every variable is used once and
once only - affine types ...at most once (PICT)
- both use the proof rules of linear/affine logic
- unique types (Clean)
- some kind of static analysis
38Unique Types of Clean
- terribly complicated (13 pages in ref man)
- uniqueness attributes of types
- fwritec Char -gt File -gt File
- uniqueness variables, polymorphism
- head uua -gt ua
- head uva -gt va, ultv