two approaches to writing interfaces
play

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


  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

  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.

  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 = ...

  4. Signature example: Ordering signature ORDERED = sig type t val lt : t * t -> bool val eq : t * t -> bool end

  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

  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 *)

  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

  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

  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

  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

  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

  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)

  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

  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)

  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”

  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

  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

  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

  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

  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?

  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

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend