• Nenhum resultado encontrado

Towards higher-order types.

N/A
N/A
Protected

Academic year: 2021

Share "Towards higher-order types."

Copied!
14
0
0

Texto

(1)

Towards Higher-Order Types

Carlos Camar~ao

Departamento de Ci^encia da Computac~ao, Universidade Federal de Minas Gerais, 31270-010 Belo Horizonte, Brasil

Luclia Figueiredo

Departamento de Computac~ao, Universidade Federal de Ouro Preto, 35400-000 Ouro Preto, Brasil

Abstract

This article explores the use of types constrained by the denition of functions of given types. This notion supports both overloading and a form of subtyping, and is related to Haskell type classes and System O. We study an extension of the Damas-Milner system, in which overloaded functions can be dened. The inference system presented uses a context-independent overloading policy, specied by means of a predicate used in a single inference rule. The treatment of overloading is less restrictive than in similar systems. Type annotations are not required, but can be used to simplify inferred types. The work motivates the use of constrained types as parameters of other, higher-order types.

1 Introduction

The problems with the treatment of overloading in languages with support for polymorphism and type inference, such as for example Miranda 11] and SML 6,9], have been discussed elsewhere 13,3]. In SML, for example, one cannot write:

square x = x * x

the reason coming from the fact that `*'is overloaded for integers and reals.

Equality is treated dierently in SML, by the introduction of a special poly-morphic type variable, constrained so that its instances must admit equality. For example, the type of a function member, that tests membership in a list,

is given as follows:

c

(2)

``a list -> ``a -> bool

In Miranda, this type is not constrained in this way, but applying member to

lists whose elements are functions generates a run-time error.

Type classes 2] are used in Haskell 4,10] to deal with problems like these. A type class is introduced to specify types of overloaded functions, as in:

class Num a where

(+), (*):: a -> a -> a

:::

and:

class Eq a where

(==):: a -> a -> bool

Instance declarations then specify which types are instances of this class, and give denitions of overloaded functions for this type, as in:

instance Num Int where (+) = PrimAddInt

:::

and:

instance Eq Int where (==) = PrimEqInt

:::

and also:

instance Eq a,b => Eq (a,b) where (a,b) == (c,d) = (a==c) & (b==d)

The last example illustrates subclassing: if equality is dened onaandb, then

it is dened on the product type (a,b).

System O 8] aimed at some improvements in relation to type classes:

 With type classes, inferred types depend on class declarations. This is in

constrast with the Damas-Milner system, for which all type declarations 2

(3)

can be removed from any typeable program and the program still remains typeable.

 Type classes cannot be dened to be formed by polymorphic type instances:

for example, one cannot dene a type class formed by product types(a,b), (a,b,c), ::: , all having, say, a projection function first.

1

System O uses universally quantied types, with possibly a constraint on the quantied variable that is a (possibly empty) set of bindings o ::  ! ,

indicating that there must exist an overloaded operator o ::p! ( :! p]),

for an instance pof . 2

The main dierence of our system in relation to System O is that we eliminate some restrictions imposed in System O in the denition and use of overloaded symbols. System O requires explicit declaration of the type of an overloaded function (our system does not). Thus, there is no inference of types of overloaded symbols in System O. In System O, the argument of an overloaded function must be of a type that is constructed from a given type constructor (including !, the constructor of function types), and all the

ar-gument type constructors of overloaded functions must be (pairwise) distinct. As an example, functions with the following types cannot be overloaded in System O:

sort: ( !!Bool)!]!]

and

sort: (!Int)!]!]

We say that a quantied constrained type is a class type. A class type

may be viewed as representing a collection of (lower-order) types, the set of its

instances, all having given (overloaded) functions that operate on parameters

of corresponding types. These instance types may be concrete and abstract types. (The implications of using class types as the type of abstract types are not dealt with in this paper, being left as a topic for further work.) In a system with type declarations, class types can be used in type annotations to simplify inferred types, as illustrated in Section 2.

The rest of the paper is organized as follows. Section 2 explores some uses of class types. Section 3 introduces the type rules of our system. Section 4 presents the type inference algorithm. Section 5 gives a semantics for terms and type expressions of System CT. Section 6 concludes.

1

Wherefirst(a,b) = a, first(a,b,c) = a, ::: 2

Thenotation:!]indicates theusualtextual substitution.

(4)

2 Examples

In this section we explore some uses of class types, using a Haskell-like nota-tion.

2.1 Types with Equality and Ordering Relations

We consider rst the possibility of dening a type that represents any type over which equality (say `==') is dened:

The equality class type is as follows:

type Eq t = t f (==):: t -> t -> Bool g

Type Int is automatically an instance of Eq t if there is a built-in

oper-ator `==' with the required type. Type Eq t can be seen | in a language

supporting class types and type denitions of this form | as an abbreviation for 8tf(==):t !t !Boolg:t .

A function member to test membership on lists of elements that can be

compared on equality can be written as follows:

member:: Eq t] -> t -> Bool member ] = false

member a (b:x) = (a==b) || member a x

Using class types, the mechanism corresponding to Haskell instance declara-tions is not used (it would correspond to an explicit declaration of subtyping).

Types with an ordering relation (say `<') are treated analogously: type Ord t = t f (<): t! t! Bool g

As with equality, type Int is an instance of Ord t if there is an operator

`<' with type Int!Int!Bool. For any t

1 that is an instance ofOrd t, type

t

1] becomes (automatically) an instance of Ord t by dening:

] < = false < ] = true

(a1:x1) < (a2:x2) = if a1<a2 then x1<x2 else false

The inferred type of this particular denition of `<' can be Ord t]! t]!

Bool. The constraint information (Ord) needs to occur only once. The type

(5)

Ord t]! t]! Boolcan be seen as an abbreviation for 8tf(<) :t!t!Boolg:t]!t]!Bool

2.2 Arithmetic Operations

For an example of a class type Numconstraining types with arithmetic

opera-tions, consider:

type Num t = t f (+), (*):: t -> t -> t

negate:: t -> t ::: g

A square function can be written as:

square:: Num t -> t square x = x * x

The type annotation is used to indicate a more specic type (a subtype), and provides an abbreviated notation, but is not required (see Section 4).

We can then write:

squares:: (Num a, Num b, Num c) -> (a, b, c) squares (x,y,z) = (square x, square y, square z)

Finally, note that class types can also be used as (super)types of abstract types, in a type system that allows the use of alternative abstract type imple-mentations for a given abstract type signature, based on the overloading of functions and constants dened in these alternative implementations.

3 Type System

We use a kernel language that is almost identical to Core-ML 5,1]. We include value constructors (k 2 K) and type constructors (C 2 C) and assume (for

simplicity) that overloaded variables are distinct from value constructors and non-overloaded variables. All lambda-bound variables are non-overloaded.

Term variables (x 2 X) are considered to be divided into three groups:

of overloaded (o 2 O), non-overloaded (u 2 U), and value constructors (k 2 K), the latter being considered as constants, having a value xed in a global

environment. Figure 1 gives the syntax of pre-terms and types of system CT. Types are modied (with respect to the type system of Core-ML) to include class types. A class type 8 fo

1 :  1 ::: o n :  n

g: represents all types :!] constrained to have functions with types o

1 :  0 1 ::: o n :  0 n, where  0 i  i :!], for i= 1::: n. 5

(6)

Terms e2E e::=xj u:ej ee 0 j letx=e in e 0 Simple Types  2T  ::= j  ! 0 j C 1 ::: n ( n 0) Types 2T  ::= j 8 fo 1 :  1 ::: o n:  n g: (n 0)

Fig. 1. Abstract Syntax of system CT

x:`x: (VAR) u: `e: 0 `u:e : ! 0 (ABS) `e: 0 ! `e 0 :  0 `ee 0 :  (APPL) `e: x: `e 0 :  `let x=e in e 0 :  (LET) o i :  i `e: `e:8 fo i :  i g: 62tv() (GEN) `e:8 fo i :  i g: `o i :  i  :!] `e::!] (INST)

Fig. 2. Type Rules of System CT

The type rules are given in Figure 2. A typing context  is a set of pairs,

written as x : . In our system, a variable x can occur more than once in a

typing context, if x 2 O. A pair x : is called a typing for x. The notation

x indicates a typing context for which it is assumed that

x does not appear

(this does not cause any restrictions due to the possibility of renaming bound variables).

The overloading policy is controlled by a predicate, used in rule (LET).

The value given by ( 1  2) is true if \  1 and 

2 can be types of overloaded

function symbols". The evaluation of ( 1  2) basically tests if  1 and  2

are not uniable for a context-independent overloading policy the following additional conditions must hold:

(7)

 the unication of argument types of functions must fail, and

 if the argument types are functional types, they must be such that they

cannot be the types of overloaded functions (in other words, unication of the argument types of the argument types must not fail).

Predicate  is dened by:

( 1  2) = 8 > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > : unify(f 1 =  2 g) fails and if  1 =  1a ! 1r and if  1 =  1 ! 0 1,  2 =  2a ! 2r, then  2 =  2 ! 0 2 unify(f 1a=  2a

g) does not fail ( 0 1  2) if  1 = 8 fo i :  i g: 0 1   2 2T ( 0 2  1) if  2 = 8 fo i :  i g: 0 2   1 2T ( 0 1  0 2) if  1 = 8 fo i :  i g: 0 1   2 = 8 0 fo 0 j :  0 j g: 0 2

Quantied type variables in a given type are assumed to be distinct from other type variables (possibly by renaming).

We also use a function tc: T ! TC, that gives the constructor of a type

given as parameter, dened below, where we let TC =Cf! g:

tc(8 fo i :  i g:) = tc() tc() = tc( ! 0) = ! tc(C 1 ::: n) = C

We use the notation x: to stand for:

(8)

x: = 8 > > > < > > > : xfx:gif x2U fx:g if x2Oand tc() =! and fx: 0 g2)( 0)

The condition \tc() =!", which implies that only functions may be

overloaded, simplies the semantics and implementation of the system, since it enables a direct unique identication of the function to be called, in a context-independent way.

We use: (xi : i)i2f1:::ng as an abbreviation for x

1 : 1::: xn :

n and assume systematically that i ranges from 1 to n, where n  0, to

write only xi : i analogously,  ` (xi : i) is used as an abbreviation

for  ` x

1 : 1 ::: 

` xn : n, and j is assumed systematically to range

from 1 to m, where m  0, to write 8jfoi : ig:  as an abbreviation for 8 1: 8 2:::: 8mfo 1 :1:::on :n g: .

The notationtv() stands for the set of free type variables of .

4 Type inference

Figure 3 presents the type inference algorithm. FunctionPP computes princi-pal pairs (type and context) for a given term, together with a set of constraints for the computed type. We write algorithms using a pattern-matching nota-tion, resembling modern functional programming languages.

Our algorithm PP works only with simple types, for simplicity. Quantied types are equivalent to simple types with a set of constraintsoi :i. We can do

this simplication because lambda-bound variables always have simple types and because we can type an expression let x = e in e

0 by rst nding the

possibly polymorphic principal typing for e, and then using instances of this type for each occurrence of xin e0.

For simplicity, we do not consider -substitutions and assume that if a variable is let-bound, then it is not lambda-bound. We can see that this assumption is important in examples for which the assumptions does not hold, like let x = 10 in x: x or true, for which PP would fail, and let x =

10 in x: x, for which PP would not give a principal typing.

Variable is used to denote a set of constraints. We use typing environ-ments A, which are sets of pairs written in the form x : ( ), where the second element is a triple (whose rst element is a simple type, the second is a set of constraints and the third is a typing context). We write A(x) for the set of triples (x xx) such that x: (x xx)2A.

A type substitution (or simply substitution) is a function from type vari-ables to types. If is a type andS is a substitution, thenSis used to denote the type obtained by replacing each free type variable in with S().

(9)

ilarly, for a typing context , the notation S denotes fx : S j x : 2g,

and for a set of constraints , the notationS denotes fo :S j o: 2 g.

Functionslcg, unify and sat, also used by algorithmPP, are dened

be-low. Function lcg computes the type that is the least common generalisation

for a set of types. Function unifycomputes the most general unifying

substi-tution for a set of equations between type expressions. Function sat( A) is

a constraint satisfaction function: it returns true if constraints are satised

in typing environment A, and false otherwise. The functions are as follows. 

lcg is dened by:

lcg(fg) = (i.e. lcg of a singleton is the single element) lcg(SfC 1 ::: n  C 0  0 1 ::: 0 n g) = if C 6C 0

then , where  is a fresh type variable else lcg(SfClcg 1 ::: lcg n g) where lcg i = lcg(f i  0 i g), fori= 1::: n

and type variables are renamed so that 

0 whenever there are  a  b with lcg(f a  b g) = and lcg(f a  b g) = 0 lcg(Sf g) =  lcg(Sf 1 ! 2  0 1 ! 0 2 g) = analogous as above,

with \!" as the type constructor

Function lcg takes into account the fact that, for example, lcg(fInt ! Int Bool ! Bool g) is  ! , for some type variable , and not  ! 

0,

for some other type variable 0

6. 

unify is given by: unify( ) = unify(EfC 1 ::: n= C 0  0 1 ::: 0 n g) = if C 6C 0 then fail else unify(Ef 1 =  0 1 :::  n =  0 n g) unify(Ef=g) = if   then unify(E)

else if  occurs in  then fail

else unify(E:!]) f7!g unify(Ef 1 ! 2 =  0 1 ! 0 2 g) =unify(Ef 1 =  0 1  2 =  0 2 g) 9

(10)

PP(uA) =

if A(u) = ( ), for some  , then ( ) else (  fu: g), where  is a fresh type variable PP(oA) = if A(o)6= then   fo :g   whereA(o) =f( i  i  i) g  = S i and  =lcg(f i g) else (  fo: g), where  is a fresh type variable PP(u:eA) = let PP(eA) = ( 0  ) in ifu: 2, for some  then ( ! 0   fu:g) else (! 0

  ), where  is a fresh type variable PP(ee 0 A) = let PP(eA)= ( ) PP(e 0 A) = ( 0  0 

0), with type variables renamed

to be dierent from those in ( ) S=unify(f= 0 j x:2 and x: 0 2 0 gf = 0 ! 00 g), where

00 is a fresh type variable in ifsat(S A) then  S 00 S S 0  SS 0  else fail PP(let x=ein e'A) = let PP(eA) = ( ) ifx2O and tc() =! and  fx: ( 0  0  0) g2A)( 0)  then A 0 = Afx: ( )g else ifx2U then A 0 = A x fx: ( )g else A 0 = in ifA 0 =

then fail else PP(e 0

A 0)

Fig. 3. Type inference for system CT



sat( A) is dened by:

For each o :  in , we have: there exists o : ( 0  0  0) in A such that unify( =

0) does not fail and, in this case, letting

S =unify(f = 0 g), sat(S 0 Afo: ( 0  0  0) g) also holds.

5 Semantics

Following 7], we use an applicative structure A that is a tuple:

A= (U A 1 U A 2 App  0 I) where:  U A 1 = fA 

g is the collection of sets A

 constructed inductively as follows:

(11)

 Base case:   C (i.e. C 1

:::

n, where

n = 0). Let K

 be the set of all

value constructors kthat yield a value in. Then A  = fI(k)j k 2K  g.  Inductive cases:   C  1 ::: n, where n > 0 and A  1 ::: A  n 2 U A 1 : let K  be the set

of all value constructors that yield a value in . Then, for all v i 2 A  i, i= 1:::arity(k),A  = S k2K c I(k)v 1 :::v arity(k).    1 !  2, where A  1 A  2 2 U A 1 : then A  = A  1 ! A  2, the set of functions from A  1 to A  2.  U A 2 = fA 

g is the collection of sets A

 constructed inductively as follows:  Base case: A  2U A 1  then A  = A  2U A 2   Inductive case: A  1 A  2 2U A 2  then A  = A  1 A  2 2U A 2 .  App  0 is the function App  0 :A ! 0 !(A  !A  0 ) fromA ! 0 to functions fromA  to A  0 , dened by: App  0 f x=f(x)  I :K! S 2U A 2 A

 assigns values to value constructors.

An environment is a mapping :Variables!U A 1   2U A 2 A 

where Variables include (term) variables and type variables, for every type

variable  we have () 2 U A

1 , and for every variable

x we have (x) 2 A ,

for some .

The meaning ]] of a type expression  in environment is dened

in-ductively as follows: ]] = ()  ! 0]] =ff j x2]] )f(x)2 0]] g C 1 ::: n]] = S fI(k)v 1 :::v n j v i 2 i]]  for alli= 1::: ng 8 fo i :  i g:]] = T f:!]]] j  is such that (o i) 2 i :!]]] g 11

(12)

The denotation of, for example,Int!Bool is a set that includes

denota-tions of overloaded funcdenota-tionsf :for whichIntis an instance of the argument

type of  and Bool is an instance of the result type of . The intersection

used in the denotation of 8fx i :i

g:  \selects" thus overloaded functions.

If  is a typing context, we say that an environment satises  if (x)2

]] , for every x :  2. The notation j=  abbreviates \ (x)2 ]] , for

every x: 2".

The meaning of a term e in an environment is dened by induction on typing derivations  `e:, for typing contexts  such that j= , as follows:

x: `x:]] = (x) `e e 0 :]] = App  0  ( `e: 0 !]] ) (`e 0 :0]] ) `u: e: ! 0]] =

the unique f 2A !B, where A= ]] and B = 

0]] , such that

for all a2AApp  0 f a= u: `e: 0]] u 7!a] `letx=e in e 0 :]] = if x2U then x:`e 0 :]] x 7!d] else x: `e 0 :]] 0 where d= `e :]] 0 = x 7!extend( (x)d)]

extend(fg) =x:if x2dom(g) then g(x) else f(x)

6 Conclusion

In extensions of the Damas-Milner type system, class types, as presented in this paper, provide a simplied treament of overloading. In system CT, the overloading policy is controlled by means of a predicate used in a single infer-ence rule. Types can be inferred, without the need for any type annotations, and there is an algorithm for computing most general typings. The use of type annotations can simplify inferred types, though.

(13)

System CT has less restrictions than existing similar systems that extend ML-like polymorphic type systems with context-independent overloading. For example, in System CT types of overloaded functions can be such that they have the same argument type constructor.

More experience is needed on the use of overloading and class types to-gether in a system with polymorphism and type inference. The main motiva-tion for our study came out from the idea of dening a form of higher-order types, that can be used as types of (lower-order) abstract and concrete types. Lower-order types would be instances of higher-order types. We intend to explore the use of class types together with higher-order types, following the idea that higher-order types are types parameterised on other, possibly con-strained types. We think this idea can enhance a functorial view of modules as parameterised types.

Further explorations of this system involve incorporating the concept of class types in a functional programming language, like Haskell or SML, study-ing the implications of this concept with regards to the subtypstudy-ing relation and program development, and studying its relation to intersection types 12].

References

1] Damas, Luis and Robin Milner. Principal type schemes for functional programs. Proc. 9th ACM Symp. on Principles of Programming Languages, pages 207{212, 1982.

2] Hall, C., K. Hammond, S.P. Jones and P. Wadler. Type Classes in Haskell. Proc. 5th European Symposium on Programming, pages 241{256, 1994. Springer LNCS 788.

3] Jones, Mark, Qualied Types. Cambridge University Press, 1994.

4] Jones, Simon Peyton et al., Report on the Programming Language Haskell. ACM SigPlan Notices, 27(5), 1992.

5] Milner, Robin, A theory of type polymorphism in programming. Journal of Computer and System Sciences, 17:348{375, 1978.

6] Milner, Robin, Mads Tofte and Robert Harper. The Denition of Standard ML. MIT Press, 1989.

7] Mitchell, J.C., Foundations for programming languages. MIT Press, 1996. 8] Odersky, M., P. WadlerM. and M. Wehr. A Second Look at Overloading.

Conference Record of Functional Programming and Computer Architecture, 1995.

9] Paulson, L.C., ML for the Working Programmer. Cambridge University Press, 1996. 2nd edition.

(14)

10] Simon Thompson. Haskell: The Craft of Functional Programming. Addison-Wesley, 1996.

11] Turner, D. A., A non-strict functional language with polymorphic types. In Proceedings of the 2nd International Conference on Functional Programming and Computer Architecture. IEEE Computer Society Press, 1985.

12] van Bakel, S., Essential Intersection Type Assignment. In R.K. Shyamasunda, editor, Proc. 13th Conf. Foundations of Software Technology and Theoretical Computer Science, LNCS 761 (1993), pp. 13{23.

13] Wadler, Philip, How to make ad-hoc polymorphism less ad hoc. Conf. Record of the 16th ACM Symposium on Principles of Programming Languages, 1989.

Referências

Documentos relacionados

Citem-se como exemplos: a ordem régia de 1685, pela qual os juízes do Fisco poderiam realizar devassas sobre os bens ocultados 28 ; o registo dos requerimentos dos familiares do

Todavia, ao analisar-se os deslocamentos executados em função do tempo de bola passada pelo distribuidor adversário, em cada da zona de ataque adversário,

Valoriza-se novamente a figura feminina pelo útero: “o cerne encantado”, último verso do poema, é a virtude de ser mulher e, por isso, ela o guarda escondendo-o. Esse

Since, data on magnetic colloids are usually reported at higher magnetic fields, we also performed additional measurements using a commercial hyperthermia equipment system

Já os indivíduos que obtêm valores baixos nesta dimensão são considerados enquanto sujeitos satisfeitos e com uma visão optimista sobre a sua vida e sobre o mundo, sendo

Problems of the output maximization (cf. Table 3) type have been dealt with in 94 papers (59.5 %), but only two types (out of seven problem types in total) have received significant

A: the three main types of esters dealt with in this review; B: two specific carboxylesterase natural substrates (acetylcholine, substrate for acetylcholinesterase and

De realçar ainda as elevadas percentagens de resistência das bactérias em estudo relativamente à vancomicina (64,5%), uma vez que os berbigões podem constituir