CS 360: Programming Languages Lecture 12: More Haskell

Similar documents
CS 360: Programming Languages Lecture 10: Introduction to Haskell

CS 320: Concepts of Programming Languages

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

An introduction introduction to functional functional programming programming using usin Haskell

Topic 6: Partial Application, Function Composition and Type Classes

Topic 6: Partial Application, Function Composition and Type Classes

Haskell Types, Classes, and Functions, Currying, and Polymorphism

Introduction to Typed Racket. The plan: Racket Crash Course Typed Racket and PL Racket Differences with the text Some PL Racket Examples

Type-indexed functions in Generic Haskell

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

Overloading, Type Classes, and Algebraic Datatypes

Haskell 101. (Version 1 (July 18, 2012)) Juan Pedro Villa Isaza

Programming with Math and Logic

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

Programming Languages Fall 2013

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

Topic 7: Algebraic Data Types

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

CIS 194: Homework 4. Due Wednesday, February 18, What is a Number?

CS 320: Concepts of Programming Languages

Structural polymorphism in Generic Haskell

CSCI-GA Scripting Languages

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

Introduction to Haskell

A general introduction to Functional Programming using Haskell

Course year Typeclasses and their instances

CS558 Programming Languages

PROGRAMMING IN HASKELL. Chapter 2 - First Steps

CSCE 314 Programming Languages

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

CSC324 Principles of Programming Languages

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

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

Functional Programming. Overview. Topics. Recall λ-terms. Examples

CS 457/557: Functional Languages

CS558 Programming Languages

CSC324 Principles of Programming Languages

CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Winter 2013

An introduction to functional programming. July 23, 2010

Shell CSCE 314 TAMU. Haskell Functions

Quiz 3; Tuesday, January 27; 5 minutes; 5 points [Solutions follow on next page]

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

Topic 9: Type Checking

Topic 9: Type Checking

CS 11 Haskell track: lecture 1

Topic 5: Higher Order Functions

Topic 5: Higher Order Functions

Advanced Topics in Programming Languages Lecture 2 - Introduction to Haskell

Haskell Overloading (1) LiU-FP2016: Lecture 8 Type Classes. Haskell Overloading (3) Haskell Overloading (2)

CIS 194: Homework 6. Due Friday, October 17, Preface. Setup. Generics. No template file is provided for this homework.

Functional Programming for Logicians - Lecture 1

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Dan Grossman Spring 2011

Monad Background (3A) Young Won Lim 11/8/17

Type families and data kinds

Introduction to Programming, Aug-Dec 2006

CSE 341 Section 7. Ethan Shea Autumn Adapted from slides by Nicholas Shahan, Dan Grossman, Tam Dang, and Eric Mullen

Lecture 19: Functions, Types and Data Structures in Haskell

Functional Programming and Haskell

CS 320: Concepts of Programming Languages

PROGRAMMING IN HASKELL. CS Chapter 6 - Recursive Functions

Topics Covered Thus Far. CMSC 330: Organization of Programming Languages. Language Features Covered Thus Far. Programming Languages Revisited

Monad Background (3A) Young Won Lim 11/18/17

According to Larry Wall (designer of PERL): a language by geniuses! for geniuses. Lecture 7: Haskell. Haskell 98. Haskell (cont) - Type-safe!

CSc 372. Comparative Programming Languages. 8 : Haskell Function Examples. Department of Computer Science University of Arizona

Types and Type Inference

CIS 194: Homework 5. Due Monday, 18 February. Expressions. (2 + 3) 4 would be represented by the value

UNIVERSITY OF CALIFORNIA Department of Electrical Engineering and Computer Sciences Computer Science Division. P. N. Hilfinger

301AA - Advanced Programming [AP-2017]

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

CSc 372 Comparative Programming Languages

Tuples. CMSC 330: Organization of Programming Languages. Examples With Tuples. Another Example

CMSC 330: Organization of Programming Languages

CIS 194: Homework 3. Due Wednesday, February 11, Interpreters. Meet SImPL

Haskell Overview III (3A) Young Won Lim 10/4/16

Principles of Programming Languages

Introduction to Functional Programming in Haskell 1 / 56

CSE399: Advanced Programming. Handout 2

Background Type Classes (1B) Young Won Lim 6/28/18

GADTs. Wouter Swierstra. Advanced functional programming - Lecture 7. Faculty of Science Information and Computing Sciences

Topics Covered Thus Far CMSC 330: Organization of Programming Languages

Advances in Programming Languages

The Typed Racket Guide

Types and Type Inference

FUNCTIONAL PROGRAMMING NO.9 TYPE AND CLASS. Tatsuya Hagino

Type Systems, Type Inference, and Polymorphism

Exercises on ML. Programming Languages. Chanseok Oh

CS 360 Programming Languages Interpreters

+ Abstract Data Types

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

Lists. Michael P. Fourman. February 2, 2010

These notes are intended exclusively for the personal usage of the students of CS352 at Cal Poly Pomona. Any other usage is prohibited without

Intro. Scheme Basics. scm> 5 5. scm>

Advanced features of Functional Programming (Haskell)

OCaml Data CMSC 330: Organization of Programming Languages. User Defined Types. Variation: Shapes in Java

CMSC 330: Organization of Programming Languages

Background Type Classes (1B) Young Won Lim 6/14/18

Functional Programming. Overview. Topics. Definition n-th Fibonacci Number. Graph

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

Transcription:

CS 360: Programming Languages Lecture 12: More Haskell Geoffrey Mainland Drexel University Adapted from Brent Yorgey s course Introduction to Haskell.

Section 1 Administrivia

Administrivia Homework 5 due Monday night (February 27). Homework 5 includes tests you can run automatically with make run-tests. Please use them! Code that does not compile will receive a zero. You can make sure that your code compiles by typing make.

Section 2 More Haskell

Pattern Matching Syntax The general syntax for pattern matching is: pat ::= _ var var@pat Constructor pat 1 pat 2... pat n (pat) area :: Shape -> Double area (Circle r) = pi * r ^ 2 area (Rectangle x1 y1 x2 y2) = abs (x2 - x1) * abs (y2 - y1) firstcircle :: [Shape] -> Shape firstcircle [] = error "Urk!" firstcircle (shape@(circle _) : _) = shape firstcircle (_:shapes) = firstcircle shapes

Case Expressions The fundamental Haskell construct for pattern matching is the case expression. In general, a case expression looks like this: case exp of pat1 -> exp1 pat2 -> exp2... In fact, the multi-clause syntax for defining functions that we have seen is just syntactic sugar for a case expression. Instead of this area :: Shape -> Double area (Circle r) = pi * r ^ 2 area (Rectangle x1 y1 x2 y2) = abs (x2 - x1) * abs (y2 - y1) we could have written this area :: Shape -> Double area shape = case shape of (Circle r) -> pi * r ^ 2 (Rectangle x1 y1 x2 y2) -> abs (x2 - x1) * abs (y2 - y1)

Tuples There is one more pervasive data type we haven t mentioned: tuples. Tuples have a single data constructor, which has a fixed arity, i.e., number of fields. There is also special syntax for tuples. anintpair :: (Int, Int) anintpair = (3, 4)

Records It is often convenient to refer to field of a data constructor by name. radius :: Shape -> Double radius (Circle r) = r radius (Rectangle ) = error "urk!" Haskell provides syntax for defining and matching on records. data Shape = Circle { centx :: Double, centy :: Double, radius :: Double} Rectangle { ulx :: Double, uly :: Double, urx :: Double, ury :: Double } mycircle = Circle 0 0 1 myothercircle = Circle { centx = 0, centy = 0, radius = 1 } radiusof circle = radius circle area (Circle { radius = r }) = pi * r ^ 2 area (Rectangle { ulx = x1, uly = y1, urx = x2, ury = y2 }) = abs (x2 - x1) * abs (y2 - y1)

Polymorphism Remember this function from the first lecture? id x = x What type do you think it has? What (not whom) can we ask? The function id has type t -> t. Here, t is a type variable. Just as variables abstract over values, type variables abstract over types.

A safe head function In Haskell, the head function raises an error and aborts execution if you give it the empty list. Let s try it. It would be nice to define a safehead that doesn t raise an error, but somehow returns a value that indicates something went wrong. Let s try to define a function safeheadints that works for lists of Ints. We will need a new data type that lets us differentiate between a normal result and the error case. data MaybeHead = NoHead JustHead Int Now, how can we write safeheadints? Let s try. safeheadints :: [Int] -> MaybeHead safeheadints [] = NoHead safeheadints (x:_) = JustHead x

Generalizing safehead But what about lists of characters? Or lists of strings? Or lists of lists of integers? We can abstract over the type of data by using a type variable! Instead of this: data MaybeHead = NoHead JustHead Int we want this: data Maybe a = Nothing Just a Now, how can we write safehead? safehead :: [a] -> Maybe a safehead [] = Nothing safehead (x:_) = Just x

Polymorphism The function safehead is polymorphic it can operate on a list containing values of any type. One important thing to note about polymorphic functions is that the caller gets to pick the types. When you write a polymorphic function, it must work for every possible input type. Why is the following function bogus? bogus :: Maybe a -> Bool bogus (Just 'x') = True bogus _ = False This functions assumes that the type a is Char the function doesn t make sense for any value of the type variable a, but only when a is Char.

Parametric Polymorphism On the other hand, this function is perfectly fine isjust :: Maybe a -> Bool isjust Nothing = False isjust (Just _) = True isjust works for any value of the type variable a it doesn t care what a is. The not caring is what the parametric in parametric polymorphism means. A function that is parametrically polymorphic in a can t do one thing when a is Int and a different thing when a is Bool Haskell simply provides no facility for writing such an operation. This property of a language is called parametricity.

Consequences of Parametricity Parametricity has many deep consequences. One consequence is something called type erasure. Because a running Haskell program can never make decisions based on type information, all the type information can be dropped during compilation. Though types are very important when writing and compiling Haskell code, they are completely irrelevant when running Haskell code. This property gives Haskell (and other statically typed languages) a huge speed boost when compared to other languages, such as Python, that need to keep types around at runtime.

Consequences of Parametricity Parametricity also restricts what functions you can write and lets you draw conclusions about a function knowing only its type. Consider the type signature for strange. How could you write this function? strange :: a -> b The strange function takes a value of some type a and produces a value of another type b. Remember, the caller gets to pick a and b. There is no way to write strange! strange = error "impossible!"

Consequences of Parametricity cont d What do you think this function does? limited :: a -> a This function must produce a value of type a when given a value of type a. There is only one way to do this, so its definition must be: limited x = x That is, if a function has type a -> a, it must be the identity function! One small catch...it could also call error or always enter an infinite loop.

List Exercises The first thing you should do when writing a function in Haskell is write down its type signature. Write map in Haskell. Write append in Haskell. Write take in Haskell. take n l should return the first n elements of the list l. Write drop in Haskell. drop n l should return the list l, but with the first n elements removed. Define a List data type that can contain any type of element.

Modules in Haskell Haskell has an extensive standard library, called the Prelude. Haskell libraries are distributed as packages, which contain one or more modules. Functions can be imported from modules like this: import Data.Char (toupper) This imports the toupper function from the Data.Char module. The parenthesized bit is optional; if it is left out, all definitions from the imported module are brought in. Read LYAH Chapter 7 for more information on modules.

Total and partial functions Consider this polymorphic type: [a] -> a. What function(s) could have this type? The Prelude function head has this type. Let s look at its source: head :: [a] -> a head (x:_) = x head [] = error "Prelude.head: empty list" It crashes on the empty list! There is no way to make up a value of an arbitrary type a, so head must crash. The function head is a partial function. Functions which are well-defined on all possible inputs are known as total functions. It is good practice to avoid partial functions in any language. This tends to be easier in Haskell than in other languages.

Replacing partial functions Partial functions like head and tail can usually be replaced by pattern matching. Consider the two following functions: dostuff1 :: [Int] -> Int dostuff1 [] = 0 dostuff1 [_] = 0 dostuff1 xs = head xs + (head (tail xs)) dostuff2 :: [Int] -> Int dostuff2 [] = 0 dostuff2 [_] = 0 dostuff2 (x1:x2:_) = x1 + x2 Both functions compute the same value, but only the second is obviously total. It is also much easier to read.

Replacing partial functions cont d Recall our function safehead safehead :: [a] -> Maybe a safehead [] = Nothing safehead (x:_) = Just x What if we know we will only use head in situations where we are guaranteed to have a non-empty list? Then we should encode this knowledge in the types we use! Let s write such a type.

Important Lessons About Types Types are invariants. Consequence: We can use types to encode invariants about programs. These invariants are checked by the compiler at compile time. Dependently typed languages have even stronger type systems that can encode arbitrary invariants. Advice: Always write down a type signature first.

Higher-order Functions in Haskell We have already seen higher-order functions like map in Haskell. Haskell programmers tend to use higher-order functions more often than many other programmers. Let s try our hand at writing a few... Notice the syntax for defining operators using prefix notation. You can read about this in LYAH Chapter 6. Why do we define $? Because ($) is parsed as an operator, and this is useful for avoiding parentheses. For example, instead of this negatenumevens1 :: [Int] -> Int negatenumevens1 x = negate (length (filter even x)) we can write this negatenumevens2 :: [Int] -> Int negatenumevens2 x = negate $ length $ filter even x Just as in Scheme, we can write anonymous functions in Haskell using lambdas. The syntax is: id :: a -> a id = \x -> x

Type Classes in Haskell We have seen parametric polymorphism. A polymorphic function like length :: [a] -> Int works for any type a. Sometimes we want functions to work for several types, but not all types. A great example of this is (+) we want to be able to add Ints and Integers and Doubles, but not Maybe Chars. This form of polymorphism is called ad-hoc polymorphism. A Haskell type class defines a set of operations. We can then choose several types that support those operations via class instances. These are not the same as object-oriented classes and instances! Intuitively, type classes correspond to sets of types which have certain operations defined for them.

The Eq Type Class Let s look at the Eq type class provided by the Prelude. class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool We can read this as follows: Eq is declared to be a type class with a single (type) parameter, a. Any type a which wants to be an instance of Eq must define two functions, (==) and (/=), with the indicated type signatures. For example, to make Int an instance of Eq, we would have to define (==) :: Int -> Int -> Bool and (/=) :: Int -> Int -> Bool. We don t actually need to do this since this instance of the type class Eq for the type Int is part of the Prelude.

Type Class Constraints Let s look at the type of (==): (==) :: Eq a => a -> a -> Bool The Eq a that comes before the => is a type class constraint. This constraint says that for any type a that is an instance of Eq, (==) can take two values of type a and return a Bool. It is a type error to call the function (==) on some type which is not an instance of Eq. A normal polymorphic type is a promise that the function will work for whatever type the caller chooses. A type with a type class constraint is a restricted promise that the function will work for any type the caller chooses, as long as the chosen type is an instance of the required type class(es). When (==) (or any type class method) is used, the compiler uses type inference to figure out which implementation of (==) should be chosen based on the inferred types of its arguments.

Defining an Instance Let s define an instance of Eq for the following type: data Foo = F Int G Char It s a bit annoying that we have to define both (==) and (/=). Fortunately, type classes can give default implementations of methods in terms of other methods, which should be used whenever an instance does not override the default definition with its own. The actual definition of Eq is: class Eq a where (==), (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y) This means we can define either (==) or (/=) the other one will be automatically defined in terms of the one we specify. However, we have to be careful: if we don t specify either one, we get infinite recursion!

Automatic Deriving As it turns out, Eq and a few other standard type classes are special: GHC is able to automatically generate instances of these classes for us. For our data type Foo, we can request that GHC automatically construct instances like this so: data Foo = F Int G Char deriving (Eq, Ord, Show) This tells GHC to automatically derive instances of the Eq, Ord, and Show type classes for our data type Foo. This deriving mechanism is baked into Haskell you can t make your own class and tell GHC how to derive instances. The full list of derivable classes is Eq, Ord, Enum, Ix, Bounded, Show, and Read. See the GHC manual for details if you re interested.

Standard type classes Ord is for types whose elements can be totally ordered, that is, where any two elements can be compared to see which is less than the other. It provides comparison operations like (<) and (<=), and also the compare function. Num is for numeric types, which support things like addition, subtraction, and multiplication. One very important thing to note is that integer literals are actually type class polymorphic: Prelude> :t 5 5 :: Num a => a This means that literals like 5 can be used as Ints, Integers, Doubles, or any other type which is an instance of Num (Rational, Complex Double, or even a type you define...) Show defines the method show, which is used to convert values into Strings. Read is the dual of Show. Integral represents whole number types such as Int and Integer.

Haskell Class vs. Java Interfaces Haskell s type classes are similar to Java interfaces. Both define a set of types/classes which implement a specified list of operations. However, Haskell type classes are more general than Java interfaces. When a Java class is defined, any interfaces it implements must be declared. Type class instances are declared separately from the declaration of the corresponding types and can even be put in a separate module.

Haskell Class vs. Java Interfaces cont d Haskell type classes can easily handle binary (or ternary, or...) methods, as in class Num a where (+) :: a -> a -> a... There is no nice way to do this in Java: for one thing, one of the two arguments would have to be the privileged one which is actually getting the (+) method invoked on it, and this asymmetry is awkward. Java s subtyping means receiving two arguments of a certain interface type does not guarantee that they are actually the same type, making implementing binary operators such as (+) awkward (usually requiring some runtime type checks).

Haskell Class vs. Java Interfaces cont d Haskell also supports multi-parameter type classes. For example, consider a hypothetical type class class Blerg a b where blerg :: a -> b -> Bool Using blerg amounts to doing multiple dispatch: which implementation of blerg the compiler should choose depends on both the types a and b. There is no easy way to do this in Java.

Implementing a Set data type Let s implement a simple abstract data type: sets. Question: what options are there for representing sets? We will represent sets as sorted lists with no duplicates. Question: can we write a Haskell type that expresses the invariant that a list is sorted? Let s get to work... P(Q), the power set of Q, is the set of all subsets of Q including the empty set and the set Q itself. Question: if Q contains n elements, how many elements are in P(Q)? Hint: use induction.