CS52 - Spring 2016 - Class 18

Example code in this lecture

   digit_addition.sml
   mutual_bad.sml
   mutual.sml
   mutual2.sml
   compose.sml

Lecture notes

  • admin
       - midterm
          - 2 pages of notes
          - review sessions tonight and wed night
       - office hours this week are different (see piazza post)

  • handling exceptions
       - Quick recap:
          - declaring exception
             exception A;

          - to raise an exception, you just put:

             raise A
             
             somewhere in the code

       - If we call a function (or do an operation) that could raise an exception, we can try and catch/handle it
          - In Java, this is done as:

          try{
             code_with_possible_exception
          }catch(name_of_exception){
             code_to_run_if_exception_happens
          }

          - In SML, it is similar, but the syntax is different

          code_with_possible_exception handle
           name_of_exception => code_to_run_if_exception_happens

          - In SML both code_with_possible_exception and code_to_run_if_exception_happens need to be expressions that represent the same type
          - If you want to handle multiple expressions (like having multiple catch statements in java), you can just list more of them using |

  • exceptions with more information
       - the exceptions we've seen so far don't have any information except that an exception has occurred
       - we can also declare exception to bring extra information along:

          exception B of string;
          exception C of int;
          exception D of (int * int);

       - These statements declare an expression that must have extra information when it is raise, e.g.
          raise B "something bad happened";
          raise C 0;
          raise D (1,-1);

       - In the handle expression, you can then access this information, e.g.

          code_with_possible_exception handle
           (B s) => ...
           | (C n) => ...
           | (D t) => ...

  • look at digit_addition.sml code
       - simple set of functions for doing digit-wise operations
          - it's a little contrived, but it allows me to illustrate the points

       - operation defines a new type for the 4 operations we will support

       - BadOperation is the exception that we will raise

       - checkDigit
          - checks to make sure it's a valid digit
          - if not raises an exception with information
          - if it is, simply returns the value

       - eval
          - evaluates an operation on two inputs
          - note that checkDigit might raise an exception
          - or we might raise it if we find divide by 0

       - eval2
          - does the same thing
          - shows an example of handling an exception without any extra information
             - DivByZero is the exception that is raised by the system if you try and divide by 0

       - calculate1
          - calls eval and handles the exception using the option type

       - calculate2
          - calls eval and returns a string
             - if it works, it has the answer
             - if there is an exception, it prints the message

  • look at mutual_bad.sml code
       - What do these two functions do?
          - determine if a number is even or odd

       - How do they do it?
          - a number is even if the number minus 1 is odd
          - a number is odd if the number minus 1 is even

       - What will happen if I try and "use" this file?
          - SML is going to complain!
          - in isEven, isOdd isn't defined yet.

       - Can I just switch the order of the two function?
          - No! Then isEven won't be defined

  • mutually recursive functions
       - These functions are what are known as mutually recursive functions

       - They are two functions that are recursive, but recurse by calling eachother, i.e. A calls B and B calls A.

       - In SML, we have to tell the compiler that the functions we're declaring are mutually recursive.

       - Look at mutual.sml code
          - to do this, instead of ending the first function with a semicolon we move on to the next line and begin the next function with "and" instead of "fun"
          - you can do this for as many functions as you'd like
          - SML will wait to examine all of the functions until a semicolon is reached.


  • Look at mutual2.sml code
       - What do these functions do?
          - select every other element from a list
          - everyOtherEven starts with the 2nd element
          - everyOtherOdd starts with the 1st element

  • function composition
       - In math, if we have two functions:
          f(x) = x+2
          g(x) = x*x

       - what is the composition of those two function (written f o g)?
          f(g(x))

          - For example, what is (f o g)(2)?
             - g(2) = 4
             - f(4) = 6
       
       - In SML, we can also compose functions using the composition operator "o" (lowercase o)

       - For SML functions f and g, what are the type requirements for us to compose them, f o g?
          - f: 'a -> 'b
          - g: 'c -> 'a

          - and the composition returns a function, of type 'c -> 'b
             

  • look at compose.sml code
       - what do the following return?
          c1 8
          c2 8
          c3 8

          - 100
          - 66
          - 100
       - what is the type of f o g?
          - int -> int (a function!)
       
       - in general, what are the type requirements for the arguments of compose and what does is the return type?
          - g: 'a -> 'b
          - f: 'b -> 'c
          - f o g: 'a -> 'c

  • parsing

  • EBNF syntax

    EBNF for numbers:

    N ::= [S]D[F]
    S ::= '+' | -
    F ::= .D
    D ::= (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9)+


  • EBNF syntax
       - EBNF = Extended Backus-Naur Form

  • For example:

       - -10
          N
          SD (N -> SD)
          -10
       - 15.2
          N
          DF (N -> DF)
          15F (D -> 15)
          15.D (F -> .D)
          15.2 (D -> 2)      
       - +12
       - 17
       - -10.542

  • Binary operations
       - 5 operators
       - work bitwise
          - 5 & 4 = 101 & 100 = 100
          - 5 | 4 = 101 & 100 = 101
          - !5 = !101 = 010

       - Some more complicated ones
          - 110 & 100 | 011 = ?
             - order of operations!
                - !
                - &
                - | and ^
                - ->
          - 110 & 101 | 100 = 100
          - !110 & (101 | 100) = 001 & 101 = 001