Two approaches to writing interfaces Interface projected from - - PowerPoint PPT Presentation

two approaches to writing interfaces
SMART_READER_LITE
LIVE PREVIEW

Two approaches to writing interfaces Interface projected from - - PowerPoint PPT Presentation

Two approaches to writing interfaces Interface projected from implementation: No separate interface Compiler extracts from implementation (CLU, Java class , Haskell) When code changes, must extract again Few cognitive


slide-1
SLIDE 1

Two approaches to writing interfaces

Interface “projected” from implementation:

  • No separate interface
  • Compiler extracts from implementation

(CLU, Java class, Haskell)

  • When code changes, must extract again
  • Few cognitive benefits

Full interfaces:

  • Distinct file, separately compiled

(Caml, Java interface, Modula, Ada)

  • Implementations can change independently
  • Full cognitive benefits
slide-2
SLIDE 2

ML module terminology

Interface is a signature Implementation is a structure Generic module is a functor

  • A compile-time function over structures
  • The point: reuse without violating abstraction

Structures and functors match signature Analogy: Signatures are the “types” of structures.

slide-3
SLIDE 3

Signature says what’s in a structure

Specify types (w/kind), values (w/type), exceptions. Ordinary type examples: type t // abstract type, kind * eqtype t type t = ... // ’manifest’ type datatype t = ... Type constructors work too type ’a t // abstract, kind * => * eqtype ’a t type ’a t = ... datatype ’a t = ...

slide-4
SLIDE 4

Signature example: Ordering

signature ORDERED = sig type t val lt : t * t -> bool val eq : t * t -> bool end

slide-5
SLIDE 5

Signature example: Integers

signature INTEGER = sig eqtype int (* <-- ABSTRACT type *) val ˜ : int -> int val + : int * int -> int val - : int * int -> int val * : int * int -> int val div : int * int -> int val mod : int * int -> int val > : int * int -> bool val >= : int * int -> bool val < : int * int -> bool val <= : int * int -> bool val compare : int * int -> order val toString : int

  • > string

val fromString : string -> int option end

slide-6
SLIDE 6

Implementations of integers

A selection. . . structure Int :> INTEGER structure Int31 :> INTEGER (* optional *) structure Int32 :> INTEGER (* optional *) structure Int64 :> INTEGER (* optional *) structure IntInf :> INTEGER (* optional *)

slide-7
SLIDE 7

What about natural numbers?

signature NATURAL = sig type nat (* abstract, NOT ’eqtype’ *) exception Negative exception BadDivisor val of_int : int -> nat val /+/ : nat * nat -> nat val /-/ : nat * nat -> nat val /*/ : nat * nat -> nat val sdiv : nat * int -> { quotient : nat, remainder : int } val compare : nat * nat -> order val decimals : nat -> int list end

slide-8
SLIDE 8

Signatures collect declarations

signature QUEUE = sig type ’a queue (* another abstract type *) exception Empty val empty : ’a queue val put : ’a * ’a queue -> ’a queue val get : ’a queue -> ’a * ’a queue (* raises Empty *) (* LAWS: get(put(a, empty)) == (a, empty) ... *) end

slide-9
SLIDE 9

Structures collect definitions

structure Queue :> QUEUE = struct (* opaque seal *) type ’a queue = ’a list exception Empty val empty = [] fun put (x,q) = q @ [x] fun get [] = raise Empty | get (x :: xs) = (x, xs) (* LAWS: get(put(a, empty)) == (a, empty) ... *) end

slide-10
SLIDE 10

Your turn! Signature for a stack

structure Stack = struct type ’a stack = ’a list exception Empty val empty = [] val push = op :: fun pop [] = raise Empty | pop (top :: rest) = (top, rest) end

slide-11
SLIDE 11

Your turn! Signature for a stack

signature STACK = sig type ’a stack exception Empty val empty : ’a stack val push : ’a * ’a stack -> ’a stack val pop : ’a stack -> ’a * ’a stack end

slide-12
SLIDE 12

Dot notation to access elements

structure Queue :> QUEUE = struct type ’a queue = ’a list exception Empty val empty = [] fun put (q, x) = q @ [x] fun get [] = raise Empty | get (x :: xs) = (x, xs) end fun single (x:’a) : ’a Queue.queue = Queue.put(Queue.empty, x)

slide-13
SLIDE 13

What interface with what implementation?

Maybe mixed together, extracted by compiler!

  • CLU, Haskell

Maybe matched by name:

  • Modula-3, Modula-3, Ada

Best: any interface with any implementation:

  • Java, Standard ML

But: not “any”—only some matches are OK

slide-14
SLIDE 14

Signature Matching

Well-formed structure Queue :> QUEUE = QueueImpl if principal signature of QueueImpl matches ascribed signature QUEUE:

  • Every type in QUEUE is in QueueImpl
  • Every exception in QUEUE is in QueueImpl
  • Every value in QUEUE is in QueueImp

(type could be more polymorphic)

  • Every substructure matches, too (none here)
slide-15
SLIDE 15

Signature Ascription

Ascription attaches signature to structure

  • Transparent Ascription: types are revealed

structure strid : sig_exp = struct_exp This method is stupid and broken (legacy) (But it’s awfully convenient)

  • Opaque Ascription: types are hidden (“sealing”)

structure strid :> sig_exp = struct_exp This method respects abstraction (And when you need to expose, can be tiresome) Slogan: “use the beak”

slide-16
SLIDE 16

Transparent Ascription

Not recommended! Example: structure IntLT : ORDERED = struct type t = int val le = (op <) val eq = (op =) end Exposed: IntLT.t = int

  • Violates abstraction
slide-17
SLIDE 17

Opaque Ascription

Recommended Example:

structure Queue :> QUEUE = struct type ’a queue = ’a list exception Empty val empty = [] fun put (x, q) = q @ [x] fun get [] = raise Empty | get (x :: xs) = (x, xs) end

Not exposed: 'a Queue.queue = 'a list

  • Respects abstraction
slide-18
SLIDE 18

How opaque ascription works

Outside module, no access to representation

  • Protects invariants
  • Allows software to evolve
  • Type system limits interoperability

Inside module, complete access to representation

  • Every function sees rep of every argument
  • Key distinction abstract type vs object
slide-19
SLIDE 19

Abstract data types and your homework

Two-player games:

  • Abstraction not as crisp as “number” or “queue”

Problems abstraction must solve:

  • Interact with human player via strings

(accept moves, display progress)

  • Know whose turn it is
  • Handle special features like “extra moves”
  • Provide API for computer player

Result: a very wide interface

slide-20
SLIDE 20

Abstraction design: Computer player

Computer player should work with any game, provided

  • Up to two players
  • Complete information
  • Always terminates

Brute force: exhaustive search Your turn! What does computer player need?

  • Types?
  • Exceptions?
  • Functions?
slide-21
SLIDE 21

Our computer player: AGS

Any game has two key types:

type config structure Move : sig type move ... (* string conversion, etc *) end

Key functions use both types:

val possmoves : config -> Move.move list val makemove : config -> Move.move -> config

Multiple games with different config, move? Yes! Using key feature of ML: functor