Thoughts on Assignment 4 Haskell: Flow of Control

Similar documents
Haskell: Lists. CS F331 Programming Languages CSCE A331 Programming Language Concepts Lecture Slides Friday, February 24, Glenn G.

Scheme: Data. CS F331 Programming Languages CSCE A331 Programming Language Concepts Lecture Slides Monday, April 3, Glenn G.

Writing an Interpreter Thoughts on Assignment 6

Writing a Lexer. CS F331 Programming Languages CSCE A331 Programming Language Concepts Lecture Slides Monday, February 6, Glenn G.

PL Categories: Functional PLs Introduction to Haskell Haskell: Functions

Scheme: Expressions & Procedures

Introduction to Syntax Analysis Recursive-Descent Parsing

CS 11 Haskell track: lecture 1

Lecture 19: Functions, Types and Data Structures in Haskell

CS 360: Programming Languages Lecture 10: Introduction to Haskell

n n Try tutorial on front page to get started! n spring13/ n Stack Overflow!

Scheme: Strings Scheme: I/O

G Programming Languages - Fall 2012

It is better to have 100 functions operate one one data structure, than 10 functions on 10 data structures. A. Perlis

Haskell Introduction Lists Other Structures Data Structures. Haskell Introduction. Mark Snyder

CSC312 Principles of Programming Languages : Functional Programming Language. Copyright 2006 The McGraw-Hill Companies, Inc.

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

CS 320: Concepts of Programming Languages

SML A F unctional Functional Language Language Lecture 19

The List Datatype. CSc 372. Comparative Programming Languages. 6 : Haskell Lists. Department of Computer Science University of Arizona

Types and Static Type Checking (Introducing Micro-Haskell)

This example highlights the difference between imperative and functional programming. The imperative programming solution is based on an accumulator

Programming Paradigms

A First Look at ML. Chapter Five Modern Programming Languages, 2nd ed. 1

Programming Languages Fall 2013

Harvard School of Engineering and Applied Sciences CS 152: Programming Languages

CS 360: Programming Languages Lecture 12: More Haskell

CS 320: Concepts of Programming Languages

A general introduction to Functional Programming using Haskell

Parsing Combinators: Introduction & Tutorial

Lists. Michael P. Fourman. February 2, 2010

SCHEME 10 COMPUTER SCIENCE 61A. July 26, Warm Up: Conditional Expressions. 1. What does Scheme print? scm> (if (or #t (/ 1 0)) 1 (/ 1 0))

Recursion and Induction: Haskell; Primitive Data Types; Writing Function Definitions

CSE 3302 Programming Languages Lecture 8: Functional Programming

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

\n is used in a string to indicate the newline character. An expression produces data. The simplest expression

Functional Programming and Haskell

CSCI-GA Scripting Languages

PROGRAMMING IN HASKELL. Chapter 2 - First Steps

The PCAT Programming Language Reference Manual

EECS 700 Functional Programming

Solution sheet 1. Introduction. Exercise 1 - Types of values. Exercise 2 - Constructors

Programming Languages Third Edition. Chapter 9 Control I Expressions and Statements

INTRODUCTION TO HASKELL

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

Logic - CM0845 Introduction to Haskell

CSCE 314 TAMU Fall CSCE 314: Programming Languages Dr. Flemming Andersen. Haskell Functions

Example Scheme Function: equal

Functional Programming Languages (FPL)

This is already grossly inconvenient in present formalisms. Why do we want to make this convenient? GENERAL GOALS

COSE212: Programming Languages. Lecture 3 Functional Programming in OCaml

Welcome Back. CSCI 262 Data Structures. Hello, Let s Review. Hello, Let s Review. How to Review 8/19/ Review. Here s a simple C++ program:

Functional Programming for Logicians - Lecture 1

An introduction introduction to functional functional programming programming using usin Haskell

CSCC24 Functional Programming Scheme Part 2

Parsing. Zhenjiang Hu. May 31, June 7, June 14, All Right Reserved. National Institute of Informatics

Syntax and Grammars 1 / 21

Haskell through HUGS THE BASICS

CS1622. Semantic Analysis. The Compiler So Far. Lecture 15 Semantic Analysis. How to build symbol tables How to use them to find

CSCE 314 Programming Languages. Functional Parsers

Chapter 15. Functional Programming Languages

An introduction to functional programming. July 23, 2010

Chapter 1 Summary. Chapter 2 Summary. end of a string, in which case the string can span multiple lines.

CMSC 330: Organization of Programming Languages. Formal Semantics of a Prog. Lang. Specifying Syntax, Semantics

JVM ByteCode Interpreter

Types and Static Type Checking (Introducing Micro-Haskell)

CS558 Programming Languages

Practical Haskell. An introduction to functional programming. July 21, Practical Haskell. Juan Pedro Villa-Isaza. Introduction.

Typed Racket: Racket with Static Types

7. Introduction to Denotational Semantics. Oscar Nierstrasz

St. MARTIN S ENGINEERING COLLEGE Dhulapally, Secunderabad

1. true / false By a compiler we mean a program that translates to code that will run natively on some machine.

Haskell 98 in short! CPSC 449 Principles of Programming Languages

CPS 506 Comparative Programming Languages. Programming Language Paradigm

CIS 194: Homework 6. Due Wednesday, 4 March. Fibonacci numbers. It s all about being lazy.

Streams and Evalutation Strategies

Option Values, Arrays, Sequences, and Lazy Evaluation

Introduction to Haskell

6.001 Notes: Section 15.1

Programming Languages Third Edition

Functional Programming in Haskell Prof. Madhavan Mukund and S. P. Suresh Chennai Mathematical Institute

Macros & Streams Spring 2018 Discussion 9: April 11, Macros

CSCE 314 TAMU Fall CSCE 314: Programming Languages Dr. Flemming Andersen. Functional Parsers

LECTURE 16. Functional Programming

Programming Languages and Techniques (CIS120)

CS 457/557: Functional Languages

1 Lexical Considerations

CSCE 314 Programming Languages. Monadic Parsing

YOLOP Language Reference Manual

Informatics 1 Functional Programming Lectures 13 and 14 Monday 11 and Tuesday 12 November Type Classes. Don Sannella University of Edinburgh

Programming with Math and Logic

Decaf Language Reference Manual

Background Operators (1E) Young Won Lim 7/7/18

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341)

CS558 Programming Languages

Imperative languages

Processadors de Llenguatge II. Functional Paradigm. Pratt A.7 Robert Harper s SML tutorial (Sec II)

To figure this out we need a more precise understanding of how ML works

Control Structures. Lecture 4 COP 3014 Fall September 18, 2017

To figure this out we need a more precise understanding of how ML works

Transcription:

Thoughts on Assignment 4 Haskell: Flow of Control CS F331 Programming Languages CSCE A331 Programming Language Concepts Lecture Slides Monday, February 27, 2017 Glenn G. Chappell Department of Computer Science University of Alaska Fairbanks ggchappell@alaska.edu 2017 Glenn G. Chappell

Thoughts on Assignment 4 Introduction In Assignment 4 you will be writing a Recursive-Descent parser, in the form of Lua module parseit. It will be similar to module rdparser4 (written in class), but it will involve a different grammar & AST specification. In Assignment 6 you will write an interpreter that takes, as input, an AST of the form your parser returns. The result will be an implementation of a programming language called Kanchil. Module parseit is to parse a complete programming language, while module rdparser4 only parses expressions. Nonetheless, Kanchil has expressions, and they work in much the same way as those handled by rdparser4. I suggest using file rdparser4.lua as a starting point for Assignment 4. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 2

Thoughts on Assignment 4 The Goal Again, here is a sample Kanchil program. # Subroutine &fibo # Given %k, set %fibk to F(%k), # where F(n) = nth Fibonacci no. sub &fibo set %a: 0 # Consecutive Fibos set %b: 1 set %i: 0 # Loop counter while %i < %k set %c: %a+%b # Advance set %a: %b set %b: %c set %i: %i+1 # ++counter end set %fibk: %a # Result end # Get number of Fibos to output print "How many Fibos to print: " input %n cr # Print requested number of Fibos set %j: 0 # Loop counter while %j < %n set %k: %j call &fibo print "F(" print %j print ") = " print %fibk cr set %j: %j + 1 end 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 3

Thoughts on Assignment 4 The Grammar (1) program stmt_list (2) stmt_list { statement } (3) statement cr (4) print ( STRLIT expr ) (5) input lvalue (6) set lvalue : expr (7) sub SUBID stmt_list end (8) call SUBID (9) if expr stmt_list { elseif expr stmt_list } [ else stmt_list ] end (10) while expr stmt_list end (11) expr comp_expr { ( && ) comp_expr } (12) comp_expr! comp_expr (13) arith_expr { ( ==!= < <= > >= ) arith_expr } (14) arith_expr term { ( + - ) term } (15) term factor { ( * / % ) factor } (16) factor ( + - ) factor (17) ( expr ) (18) NUMLIT (19) ( true false ) (20) lvalue (21) lvalue VARID [ [ expr ] ] 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 4

Thoughts on Assignment 4 Calling lexit.preferop Your parser will need to call function lexit.preferop immediately after it sees a lexeme that fits any of the following. category == VARID category == NUMLIT string == "]" string == ")" string == "true" string == "false" I suggest that you handle this in function advance. Then this issue does not have to be dealt with in your parsing functions at all. You can write it once, and never worry about it again. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 5

Thoughts on Assignment 4 Returning Two Values The most common mistake I made when writing module parseit was to return only one value from a parsing function. Remember that a parsing function will always return two values: boolean & AST. If the boolean is True, then the AST needs to be in the proper form. If the boolean is False, then the AST can be anything (it might as well be nil). 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 6

Thoughts on Assignment 4 Parsing end Three kinds of Kanchil statements are terminated with an end keyword: Sub statements (subroutine definitions) If statements While statements (while loops) Other kinds of statements have no end. This means, for example, that you generally do not want to exit the parsing function early if you think the if-statement is over. So code like the following is not good. if not matchstring("else") then end return true, ast 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 7

Thoughts on Assignment 4 Some Code I have posted a file containing a portion of my version of parseit.lua. See assn4_code.txt. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 8

Review PL Categories: Functional PLs Functional programming (FP) is a programming style in which functions are primary, and side effects & mutable data are avoided. A functional programming language is a PL designed to support FP well. A pure functional PL does not support mutable data at all. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 9

Review Introduction to Haskell Haskell is a pure functional PL. It has first-class functions and good support for higher-order functions. Haskell has a sound static type system with sophisticated type inference. Typing is largely inferred, and thus implicit; however, type annotations are permitted. Haskell has no iteration. Recursion is used. Tail-call optimization (TCO) is done. Haskell has significant indentation. Evaluation in Haskell is lazy. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 10

Review Haskell: Functions [1/2] Function definition: what looks like a function call, an equals sign, and an expression for the value of the function. Pattern matching is used. Introduce local definitions with where. Patterns factorial 0 = 1 factorial n = n * factorial prev prev = n-1 Local definition where We can also define new infix binary operators. a +$+ b = 2*a + b 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 11

Review Haskell: Functions [2/2] Currying: simulating a multiparameter function using a single parameter function that returns a function. sub a b = a-b sub 5 2 -- Returns 3 sub_from_5 = sub 5 sub_from_5 2 -- Returns 3 Currying makes some higher-order functions easy to write. rev f a b = f b a rsub = rev sub rsub 5 2 -- Returns -3 rsub 2 5 -- Returns 3 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 12

Review Haskell: Lists Lists & Tuples [1/2] A statically typed PL will typically support two ways of aggregating multiple data items into a single collection: A collection of an arbitrary number of data items, all of the same type. Example. C++ vector, list, deque. A collection of a fixed number of data items, possibly of different types. Example. C++ tuple, struct. Haskell supports the above two categories as well, in the form of lists and tuples. A Haskell tuple holds a fixed number of data items, possibly of different types. > :t (2.1, 1.2, True) (Double,Double,Bool) This represents the GHCi prompt. For code, see list.hs. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 13

Review Haskell: Lists Lists & Tuples [2/2] A Haskell list holds an arbitrary number of data items, all of the same type. A list literal uses brackets and commas. ["hello", "there"] -- List of two String values [[1], [], [1,2,3,4]] -- List of lists of Integer [1, [2, 3]] -- ERROR; types differ [1, 3..] -- Infinite list The type of a list is written as the item type in brackets. > :t [True, False] [True, False] :: [Bool] > :t [False, True, True, True, True, False] [False, True, True, True, True, False] :: [Bool] 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 14

Review Haskell: Lists List Primitives 1. Construct an empty list. [] 2. Cons: list from first item, list of other items. Uses colon (:). 5:[2, 1, 8] -- Same as [5, 2, 1, 8] 5:2:1:8:[] -- Also same; ":" is right-associative 3. Pattern matching for lists. ff [] = 3 -- Value of ff for an empty list ff (x:xs) = 4 -- Value of ff for a nonempty list gg [a, b, c] = 19 -- Value of gg for a 3-item list 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 15

Review Haskell: Lists Other List Syntax: Strings & Ranges A Haskell String is a list of characters (Char values). ['a', 'b', 'c'] "abc" -- Same as above Use.. to construct a list holding a range of values. There are exactly four ways to do this. [1..10] -- Same as [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [1,3..10] -- Same as [1, 3, 5, 7, 9] [1..] -- Infinite list: [1, 2, 3, 4, 5, 6, 7, 8, ] [1,3..] -- Infinite list: [1, 3, 5, 7, 9, 11, ] These four are wrappers around enumfromto, enumfromthento, enumfrom, and enumfromthen, respectively. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 16

Review Haskell: Lists Other List Syntax: List Comprehensions [1/2] You have probably seen the mathematical notation known as a set comprehension (or set-builder notation). Here is an example. { xy x {3, 2, 1} and y {10, 11, 12} } The above is read as, The set of all xy for x in the set {1, 2, 3} and y in the set {10, 11, 12}. A number of PLs, including Haskell, have a construct based on this idea: the list comprehension. Here is a Haskell example. [ x*y x <- [3, 2, 1], y <- [10, 11, 12] ] This deals with Haskell lists instead of sets, but is otherwise very similar to the above set comprehension. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 17

Review Haskell: Lists Other List Syntax: List Comprehensions [2/2] The syntax of a Haskell list comprehension is as follows. Brackets enclose the following: An expression. Then a vertical bar ( ). Then a comma-separated list of two kinds of things: var <- list Expression of type Bool Here are some examples: > [ x*y x <- [3, 2, 1], y <- [10, 11, 12] ] [30,33,36,20,22,24,10,11,12] > [ x*x x <- [1..6] ] [1,4,9,16,25,36] > [ x x <- [1..20], x `mod` 2 == 1] [1,3,5,7,9,11,13,15,17,19] 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 18

Review Haskell: Lists Lists & Recursion [1/3] When writing a function that takes a list, it is very common to have two cases. One case handles the empty list: []. The other case handles nonempty lists. Remember that a pattern like a:as matches nonempty lists. isempty [] = True isempty (x:xs) = False listlength [] = 0 listlength (x:xs) = 1 + listlength xs 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 19

Review Haskell: Lists Lists & Recursion [2/3] A function that takes a list will often be recursive. Such a function will usually be organized as follows. The version that handles the empty list ([]) will be the base case. The version that handles nonempty lists (b:bs) will be the recursive case. This will do a computation involving the head of the list (b) and make a recursive call with the tail (bs). myfilter p [] = [] -- p for predicate: function -- returning Bool myfilter p (x:xs) = if (p x) then x:rest else rest where rest = myfilter p xs Note the if then else construction. We can put line breaks pretty much anywhere we want inside this construction. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 20

Review Haskell: Lists Lists & Recursion [3/3] Sometimes other kinds of recursion are used. Here is a function that does lookup by index in a list (zero-based). lookup 0 (x:xs) = x lookup n (x:xs) = lookup (n-1) xs lookup _ [] = error "lookup: index too big or negative" This pattern means unused parameter. Function error takes a String and returns any type; that is, it can be used in any context. It does not actually return anything. Instead, it crashes the program, printing a message that includes the given String. An alternate error-message function is undefined, which takes no parameters. It is like error with a default message. lookup _ [] = undefined -- Replaces the above line 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 21

Haskell: Flow of Control Introduction Flow of control refers to the ways a PL determines what code is executed. For example, flow of control in Lua includes: Selection (if elseif else). Iteration (while, for). Function calls. Coroutines. Threads. Exceptions. Haskell has very different flow-of-control facilities from most imperative PLs. Key Idea. Things that are done with traditional flow-of-control constructs in imperative programming languages are often done differently in Haskell. For code, see flow.hs. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 22

Haskell: Flow of Control Pattern Matching, Recursion, Lazy Evaluation [1/5] We have seen that Haskell has a useful pattern matching facility, which allows us to choose one of a number of function definitions. The rule is that the first definition with a matching pattern is the one used. isempty [] = True isempty (x:xs) = False -- fibo the SLOW way fibo 0 = 0 fibo 1 = 1 fibo n = fibo (n-2) + fibo (n-1) In many of the places we would use an if else construction in an imperative PL, we use pattern matching in Haskell. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 23

Haskell: Flow of Control Pattern Matching, Recursion, Lazy Evaluation [2/5] Haskell also makes heavy use of recursion. Recursion can be less costly in Haskell than in PLs like C++, because of Haskell s required tail-call optimization (TCO). TCO means that a tail call does not use additional stack space. listlength [] = 0 listlength (x:xs) = 1 + listlength xs In places where we would use a loop in an imperative PL, we use recursion in Haskell. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 24

Haskell: Flow of Control Pattern Matching, Recursion, Lazy Evaluation [3/5] By default, Haskell does lazy evaluation. This allows for infinite lists. We have said that we can generate these even without the.. syntax. But how? Here is an idea. -- listfrom n -- Returns the infinite list [n, n+1, n+2, ]. listfrom n = n:listfrom (n+1) Is this code acceptable? It has recursion without a base case. But this is not a problem, thanks to lazy evaluation. A recursive call is only made if further list items are needed. Using only a finite number of items guarantees that the recursion terminates. The above code uses corecursion: A stream of values is generated recursively. The recursion terminates when no more values are needed. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 25

Haskell: Flow of Control Pattern Matching, Recursion, Lazy Evaluation [4/5] Something else we can do: write our own if else, as a function. -- myif condition tval fval -- Returns tval if condition is True, fval otherwise. myif True tval _ = tval myif False _ fval = fval Note that no more than one of tval, fval is ever evaluated, thanks to lazy evaluation. Here is the slow Fibonacci algorithm using myif. fibo n = myif (n <= 1) n (fibo (n-2) + fibo (n-1)) 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 26

Haskell: Flow of Control Pattern Matching, Recursion, Lazy Evaluation [5/5] And here is myfilter, reimplemented using myif. myfilter p [] = [] myfilter p (x:xs) = myif (p x) (x:rest) rest where rest = myfilter p xs It turns out that the combination of pattern matching, recursion, and lazy evaluation, together with function calls, are all we need. We can build any flow-of-control construct out of these. However, Haskell has other flow-of-control facilities, for convenience. Next we look at a few of these. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 27

Haskell: Flow of Control Selection Introduction Selection allows us to choose one of multiple options to execute. Selection in C++ includes if else, switch, and virtual function dispatch. In Haskell, pattern matching works as a selection mechanism. Other selection constructions include guards, if then else, and case. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 28

Haskell: Flow of Control Selection Guards [1/3] Guards are the Haskell equivalent of mathematical notation like the following. x, if x 0; myabs x = ( x, otherwise. In Haskell: myabs x x >= 0 = x otherwise = -x We use guards in situations that pattern matching cannot handle. For example, there is no pattern that matches only nonnegative numbers. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 29

Haskell: Flow of Control Selection Guards [2/3] myabs x x >= 0 = x otherwise = -x Note that there is no equals sign after the first line above. Each vertical bar is followed by a boolean expression. The first True expression tells which value is used. We generally want the last line to handle all remaining cases. We could use True as our final expression. otherwise is a variable with value True. Here is the slow Fibonacci algorithm reimplemented using guards. fibo n n <= 1 = n otherwise = fibo (n-2) + fibo (n-1) 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 30

Haskell: Flow of Control Selection Guards [3/3] Here is myfilter reimplemented using guards. myfilter p [] = [] myfilter p (x:xs) p x = x:rest otherwise = rest where rest = myfilter p xs 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 31

Haskell: Flow of Control Selection if then else We have seen Haskell s if then else construction. This is much like our myif. myif condition tval fval if condition then tval else fval -- Same as above Most of the possible uses of if then else are probably better done with guards. And some people consider if then else to be un-haskell-ish. But use it if you want. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 32

Haskell: Flow of Control Selection case Haskell s case construction is analogous to switch in C++. Here is an example, using case and using multiple function definitions. -- case fibo n = case n of 0 -> 0 1 -> 1 _ -> fibo (n-2) + fibo (n-1) -- Multiple definitions fibo 0 = 0 fibo 1 = 1 fibo n = fibo (n-2) + fibo (n-1) A case construction can always be replaced by multiple definitions. So I never use case. But case does have an important behind-thescenes role. Multiple definitions are actually syntactic sugar over a case construction. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 33

Haskell: Flow of Control Error Handling Fatal Errors We have seen Haskell s fatal-error facilities: error and undefined. lookup 0 (x:xs) = x lookup n (x:xs) = lookup (n-1) xs lookup _ [] = error "lookup: index too big or negative" These should be reserved for cases when a program needs to crash. This is generally because the program has detected a bug in its code. It crashes with an explanatory message, so that a developer can fix the bug. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 34

Haskell: Flow of Control Error Handling Exceptions Haskell also has an exception mechanism, for dealing with error conditions that may be handled at runtime. I think that Haskell s exceptions are best avoided by new Haskell programmers (and many experienced Haskell programmers). So we will not be covering them. If you look into Haskell s exceptions, be aware that the terms error and exception are used inconsistently in both the standard library naming conventions and its documentation. Some documentation authors have difficulty explaining the difference between the two terms; others seem unaware that there is a difference. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 35

Haskell: Flow of Control Error Handling Implementing Exceptions [1/6] As with other Haskell features, it can be interesting to see how we might implement something like exceptions ourselves. We can do a rudimentary exception implementation using maybe types. Suppose t is a Haskell type. The type Maybe t has two kinds of values: Just x, where x is a value of type t, and Nothing. We can distinguish between these using pattern matching. squaremaybe (Just x) = Just (x*x) squaremaybe Nothing = Nothing Just and Nothing are examples of Haskell constructors. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 36

Haskell: Flow of Control Error Handling Implementing Exceptions [2/6] Suppose we use Just x to represent the value x and Nothing to represent an exception. esqrt :: Double -> Maybe Double esqrt x x < 0.0 = Nothing otherwise = Just (sqrt x) It would probably be better if our function took the same kind of value that it returns 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 37

Haskell: Flow of Control Error Handling Implementing Exceptions [3/6] It would probably be better if our function took the same kind of value that it returns. esqrt :: Maybe Double -> Maybe Double esqrt Nothing = Nothing esqrt (Just x) x < 0.0 = Nothing otherwise = Just (sqrt x) Now the function not only raises exceptions; it also propagates exceptions it receives to its caller. (This is analogous to what is called exception neutrality in C++.) 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 38

Haskell: Flow of Control Error Handling Implementing Exceptions [4/6] Here is a division operator that uses this idea. (I use @/, since / is already taken.) infixl 7 @/ -- Sets precedence, left associativity (@/) :: Maybe Double -> Maybe Double -> Maybe Double Nothing @/ _ = Nothing _ @/ Nothing = Nothing (Just _) @/ (Just 0.0) = Nothing (Just x) @/ (Just y) = Just (x / y) 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 39

Haskell: Flow of Control Error Handling Implementing Exceptions [5/6] We can write other operators this way. A few conveniences: e1 = Just 1.0 e2 = Just 2.0 e3 = Just 3.0 printit (Just x) = show x printit Nothing = "ERROR!" Conversion to String Examples: Function application, but very low precedence > printit $ e1 @/ e2 0.5 > printit $ e3 @+ e1 @/ (e3 @- e1 @- e2) * e2 "ERROR!" 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 40

Haskell: Flow of Control Error Handling Implementing Exceptions [6/6] We could improve this arithmetic package just a bit. Using type classes, we can overload the / operator, and thus avoid using @. We can also overload the show function, so that our values can be printed in the usual way. There might be an objection to my referring to the error signals used by this package as exceptions. They are normal return values. Why call them exceptions? My answer is that, because of the way the various functions & operators are written, error signals propagate automatically to the caller. Once an error has been found, no more arithmetic is done (because of lazy evaluation, no more arithmetic at all); the error signal is simply sent to the caller. While this mechanism may not be exceptions in some precise sense, it acts enough like exceptions as we know them, to warrant the name (I think). 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 41

Haskell: Flow of Control TO BE CONTINUED Haskell: Flow of Control will be continued next time. 27 Feb 2017 CS F331 / CSCE A331 Spring 2017 42