CSE230 THURSDAY FEBRUARY 19, 1998 PACKAGES Abstract data types are generalized in modern languages (e.g. Ada) to so-called "packages". A package has a public signature and a private implementation, like an ADT. The difference is that a package can simultaneously provide many types, many specific values, and many operations. This increases flexibility and allows mutually dependent types but loses the analogy with concrete data types. Packages preserve the crucial ADT idea of "information-hiding." Details that a user does not need to know are not visible to him. Packages are best-known in Ada. ML signatures and structures are in fact packages also. Ada and ML both deal with parametrized ADTs and packages. OBJECT-ORIENTED PROGRAMMING BACKGROUND Conceptually this is a natural step forward from abstract data types and packages, especially in imperative languages. However historically and sociologically it is not. ADTs belong to the formalist tradition, represented by people like Tony Hoare, David Gries, and Barbara Liskov. This tradition favors - formal specifications - a sharp distinction between operators and values (in contrast to Lis which has the same syntax for pprograms and data) - verification and type-checking rather than debugging OOP belongs to the hacker tradition, with Lisp and to some extent, C. The ideas come from Simula 67, an Algol family language invented for discrete event simulation in Norway in 1967. In the 1970s OOP was taken to the extreme in Smalltalk at Xerox PARC, led by Adele Goldberg. Smalltalk is more different in syntax and in programming environment than in language. OOP does have connections to the formalist tradition, through the ideas of layering and information-hiding. See Structured programming, by O.-J. Dahl, E. W. Dijkstra and C. A. R. Hoare. London, New York, Academic Press, 1972. THE LUDDITE VIEW OF OOP Some people think that object-oriented programming is a radical, fundamental innovation in programming languages. Those people will call the point of view advanced here Luddite. % webster luddite Ludd-ite \'led-,uEt\ n [Ned Ludd supposed Leicestershire workman who destroyed machinery] (1811) :one of a group of early 19th century English workmen destroying laborsaving machinery as a protest; broadly :one who is opposed to esp. technological change When all you have is a hammer, everything looks like a nail. The problem with C++ is that everything is a thumb. The CSE faculty have just made the tentative decision to drop C++ as the main undergraduate teaching language, in favor of Java and C. Many undergraduates do not understand when to use C, and when to use a higher-level language. They just view C and C++ as bigger and smaller languages. Rather, C++ is higher-level and C is lower-level. C++ is for bigger projects and C is for smaller platforms. WHAT IS AN OBJECT? An "object" is a variable with implementation-independence. An object contains data, called its "state" or private variables, and it contains procedures that can update its private variables. An internal procedure of an object is called a method. Invoking a method is called "sending a message". Objects are thought of as active individuals rather than passive values. Instead of writing x := plus(x,y) write send x "add y" i.e. "send the message add y to the object x". A class is a type of objects. An object is an "instance of" its class. AN INFORMAL EXAMPLE The definition of a class looks similar to the implementation of an ADT. Typically no separate signature or specification is given. class financial-history = begin state cash, receipts, expenses method init = cash := 0; receipts := []; expenses := []; method receive (amount) = receipts := amount :: receipts; cash := cash + amount; method spend (amount) = expenses := amount :: expenses; cash := cash - amount; method cash? () = return cash; end; After a class declaration, individual objects can be declared in the same way as variables. If a class has an "init" method, it is invoked automatically whenever an object of the class is created. INHERITANCE An ADT is a collection of values defined implicitly by a set of primitive operations whose implementation is hidden from the user. So is a class. The main difference is that objects are almost always mutable values. From a programming language (as opposed to programming methodology) point of view, the major innovation in OOP is inheritance. A child class that inherits from a parent class is also called a derived class. For example: class tax-history parent financial-history state deductions; method init = deductions := []; method deductible-spend (amount) = deductions := amount :: deductions; send self spend(amount); Every object of type tax-history has three financial-history state variables, plus one new state variable. It can respond to all financial-history messages, and some new messages. RESTRICTING VISIBILITY In Java the programmer can choose four levels of visibility for the components (i.e the state variables and methods) of an object. public any user of the object can access the component friend any class in the same package have access protected only subclasses can access the component private only methods in this class have access The default is "friend". Traditional abstract data types only allow the "public" and "private" possibilities. The visibilities "friend" and "protected" take into account the rolee of classes as modules of separate implementation and compilation. One important use for packages is as units of separate compilation. All the types in a package typically provide related functionality and are implemented by the same person or team, so it is reasonable for the classes in a package to have access to each other's implementation, i.e. to all the state variables and methods of each other. BINDING OF METHODS TO NAMES One method of an object can call another method of the same object if necessary. When a method is viewed as a procedure, its implicit first argument is the object itself to which the method belongs. The name "self" refers to this current object. A call to a self-method should refer to the method with the given name in the child class. This allows child class methods to override parent methods. For example, sending the message id to an object of class D with the following class definitions should give "I'm a type D object". class C = begin method whoami = return "I'm a type C object" method id = print (send self whoami) end; class D = begin method whoami = return "I'm a type D object" end; Under standard static scoping, the name "whoami" in the body of the method "C.id" would refer to the method "C.whoami". This is not what we want. Method names should follow what is called "dynamic binding": the method corresponding to a name should be found in the environment of the self object, not in the definition-time environment. Exercise: how should definitions of classes, objects, and/or methods be viewed as blocks in order to make dynamic binding be dynamic scoping?