cse 341 programming languages
play

CSE 341 : Programming Languages For larger programs, one top-level - PowerPoint PPT Presentation

Modules CSE 341 : Programming Languages For larger programs, one top-level sequence of bindings is poor Especially because a binding can use all earlier (non- Lecture 12 shadowed) bindings ML Modules So ML has structures to define


  1. Modules CSE 341 : Programming Languages For larger programs, one “top-level” sequence of bindings is poor – Especially because a binding can use all earlier (non- Lecture 12 shadowed) bindings ML Modules So ML has structures to define modules structure MyModule = struct bindings end Inside a module, can use earlier bindings as usual – Can have any kind of binding (val, datatype, exception, ...) Zach Tatlock Outside a module, refer to earlier modules’ bindings via ModuleName.bindingName Spring 2014 – Just like List.foldl and String.toUpper ; now you can define your own modules 2 Example Namespace management structure MyMathLib = • So far , this is just namespace management struct – Giving a hierarchy to names to avoid shadowing – Allows different modules to reuse names, e.g., map fun fact x = – Very important, but not very interesting if x=0 then 1 else x * fact(x-1) val half_pi = Math.pi / 2 fun doubler x = x * 2 end 3 4

  2. Optional: Open Signatures • A signature is a type for a module • Can use open ModuleName to get “direct” access to a – What bindings does it have and what are their types module’s bindings • Can define a signature and ascribe it to modules – example: – Never necessary; just a convenience; often bad style signature MATHLIB = – Often better to create local val-bindings for just the bindings sig you use a lot, e.g., val map = List.map val fact : int -> int • But doesn’t work for patterns val half_pi : real val doubler : int -> int • And open can be useful, e.g., for testing code end structure MyMathLib :> MATHLIB = struct fun fact x = … val half_pi = Math.pi / 2.0 fun doubler x = x * 2 end 5 6 In general Hiding things • Signatures Real value of signatures is to to hide bindings and type definitions signature SIGNAME = – So far, just documenting and checking the types sig types-for-bindings end – Can include variables, types, datatypes, and exceptions defined Hiding implementation details is the most important strategy for in module writing correct, robust, reusable software • Ascribing a signature to a module structure MyModule :> SIGNAME = So first remind ourselves that functions already do well for some struct bindings end forms of hiding … – Module will not type-check unless it matches the signature, meaning it has all the bindings at the right types – Note: SML has other forms of ascription; we will stick with these [opaque signatures] 7 8

  3. Hiding with functions Example Outside the module, MyMathLib.doubler is simply unbound These three functions are totally equivalent: no client can tell which – So cannot be used [directly] we are using (so we can change our choice later): – Fairly powerful, very simple idea fun double x = x*2 fun double x = x+x signature MATHLIB = val y = 2 sig fun double x = x*y val fact : int -> int val half_pi : real Defining helper functions locally is also powerful end – Can change/remove functions later and know it affects no structure MyMathLib :> MATHLIB = other code struct fun fact x = … Would be convenient to have “private” top-level functions too val half_pi = Math.pi / 2.0 – So two functions could easily share a helper function fun doubler x = x * 2 – ML does this via signatures that omit bindings … end 9 10 A larger example [mostly see the code] Library spec and invariants Now consider a module that defines an Abstract Data Type (ADT) Properties [externally visible guarantees, up to library writer] – A type of data and operations on it – Disallow denominators of 0 Our example: rational numbers supporting add and toString – Return strings in reduced form (“4” not “4/1”, “3/2” not “9/6”) – No infinite loops or exceptions structure Rational1 = struct Invariants [part of the implementation, not the module’s spec] datatype rational = Whole of int | Frac of int*int – All denominators are greater than 0 exception BadFrac – All rational values returned from functions are reduced (*internal functions gcd and reduce not on slide*) fun make_frac (x,y) = … fun add (r1,r2) = … fun toString r = … end 11 12

  4. More on invariants A first signature With what we know so far, this signature makes sense: Our code maintains the invariants and relies on them – gcd and reduce not visible outside the module Maintain: signature RATIONAL_A = – make_frac disallows 0 denominator, removes negative sig denominator, and reduces result datatype rational = Whole of int | Frac of int*int – add assumes invariants on inputs, calls reduce if needed exception BadFrac val make_frac : int * int -> rational val add : rational * rational -> rational Rely: val toString : rational -> string – gcd does not work with negative arguments, but no end denominator can be negative – add uses math properties to avoid calling reduce structure Rational1 :> RATIONAL_A = … – toString assumes its argument is already reduced 13 14 The problem So hide more By revealing the datatype definition, we let clients violate our invariants Key idea: An ADT must hide the concrete type definition so clients by directly creating values of type Rational1.rational cannot create invariant-violating values of the type directly – At best a comment saying “must use Rational1.make_frac ” Alas, this attempt doesn’t work because the signature now uses a signature RATIONAL_A = type rational that is not known to exist: sig datatype rational = Whole of int | Frac of int*int signature RATIONAL_WRONG = … sig exception BadFrac val make_frac : int * int -> rational Any of these would lead to exceptions, infinite loops, or wrong results, val add : rational * rational -> rational which is why the module’s code would never return them val toString : rational -> string – Rational1.Frac(1,0) end – Rational1.Frac(3,~2) structure Rational1 :> RATIONAL_WRONG = … – Rational1.Frac(9,6) 15 16

  5. Abstract types This works! (And is a Really Big Deal) signature RATIONAL_B = So ML has a feature for exactly this situation: sig type rational In a signature: exception BadFrac type foo val make_frac : int * int -> rational val add : rational * rational -> rational means the type exists, but clients do not know its definition val toString : rational -> string signature RATIONAL_B = end sig Nothing a client can do to violate invariants and properties: type rational exception BadFrac – Only way to make first rational is Rational1.make_frac val make_frac : int * int -> rational – After that can use only Rational1.make_frac , val add : rational * rational -> rational Rational1.add , and Rational1.toString val toString : rational -> string – Hides constructors and patterns – don’t even know whether end or not Rational1.rational is a datatype structure Rational1 :> RATIONAL_B = … – But clients can still pass around fractions in any way 17 18 Two key restrictions A cute twist In our example, exposing the Whole constructor is no problem So we have two powerful ways to use signatures for hiding: In SML we can expose it as a function since the datatype binding in the module does create such a function 1. Deny bindings exist (val-bindings, fun-bindings, constructors) – Still hiding the rest of the datatype 2. Make types abstract (so clients cannot create values of them or – Still does not allow using Whole as a pattern access their pieces directly) signature RATIONAL_C = sig (Later we will see a signature can also make a binding’s type more type rational specific than it is within the module, but this is less important) exception BadFrac val Whole : int -> rational val make_frac : int * int -> rational val add : rational * rational -> rational val toString : rational -> string end 19 20

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