PROGRAMMING IN HASKELL

Slides:



Advertisements
Similar presentations
15-Jan-15 More Haskell Functions Maybe, Either, List, Set, Map.
Advertisements

Haskell Chapter 7. Topics  Defining data types  Exporting types  Type parameters  Derived instances  Type synonyms  Either  Type classes  Not.
ML Datatypes.1 Standard ML Data types. ML Datatypes.2 Concrete Datatypes  The datatype declaration creates new types  These are concrete data types,
0 PROGRAMMING IN HASKELL Chapter 10 - Declaring Types and Classes.
Functional Programming Universitatea Politehnica Bucuresti Adina Magda Florea
Grab Bag of Interesting Stuff. Topics Higher kinded types Files and handles IOError Arrays.
Advanced Programming Handout 12 Higher-Order Types (SOE Chapter 18)
Cse536 Functional Programming 1 6/23/2015 Lecture #17, Dec. 1, 2004 Todays Topics – Higher Order types »Type constructors that take types as arguments.
0 PROGRAMMING IN HASKELL Typeclasses and higher order functions Based on lecture notes by Graham Hutton The book “Learn You a Haskell for Great Good” (and.
CS2110: SW Development Methods Textbook readings: MSD, Chapter 8 (Sect. 8.1 and 8.2) But we won’t implement our own, so study the section on Java’s Map.
Haskell Chapter 8. Input and Output  What are I/O actions?  How do I/O actions enable us to do I/O?  When are I/O actions actually performed?
Advanced Functional Programming 2009 Ulf Norell (lecture by Jean-Philippe Bernardy)
CS 206 Introduction to Computer Science II 02 / 13 / 2009 Instructor: Michael Eckmann.
0 Functors in Haskell Adapted from material by Miran Lipovaca.
16-Nov-15 More Haskell Functions Maybe, Either, List, Set, Map.
© M. Winter COSC 4P41 – Functional Programming Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there.
0 Odds and Ends in Haskell: Folding, I/O, and Functors Adapted from material by Miran Lipovaca.
Grab Bag of Interesting Stuff. Higher Order types Type constructors are higher order since they take types as input and return types as output. Some type.
CMSC 330: Organization of Programming Languages Operational Semantics.
More Data Types CSCE 314 Spring CSCE 314 – Programming Studio Defining New Data Types Three ways to define types: 1.type – Define a synonym for.
0 PROGRAMMING IN HASKELL Typeclasses and higher order functions Based on lecture notes by Graham Hutton The book “Learn You a Haskell for Great Good” (and.
Advanced Functional Programming 2010
Polymorphic Functions
Haskell Chapter 8.
Haskell Chapter 7.
String is a synonym for the type [Char].
Recursion.
Types CSCE 314 Spring 2016.
Binary Search Trees One of the tree applications in Chapter 10 is binary search trees. In Chapter 10, binary search trees are used to implement bags.
Theory of Computation Lecture 4: Programs and Computable Functions II
More Haskell Functions
More Haskell Functions
Map interface Empty() - return true if the map is empty; else return false Size() - return the number of elements in the map Find(key) - if there is an.
PROGRAMMING IN HASKELL
Subroutines Idea: useful code can be saved and re-used, with different data values Example: Our function to find the largest element of an array might.
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
Haskell Strings and Tuples
PROGRAMMING IN HASKELL
Binary Search Trees One of the tree applications in Chapter 10 is binary search trees. In Chapter 10, binary search trees are used to implement bags.
Haskell Dealing with impurity 30-Nov-18.
CISC101 Reminders Assn 3 due tomorrow, 7pm.
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
More Haskell Functions
PROGRAMMING IN HASKELL
Type & Typeclass Syntax in function
CSE341: Programming Languages Lecture 12 Equivalence
Advanced Functional Programming
Types and Classes in Haskell
PROGRAMMING IN HASKELL
Fundamentals of Functional Programming
RECURSION Haskell.
More Haskell Functions
Haskell Dealing with impurity 8-Apr-19.
Records and Type Classes
Grab Bag of Interesting Stuff
PROGRAMMING IN HASKELL
Defining New Data Types
Programming Languages
Theory of Computation Lecture 4: Programs and Computable Functions II
Haskell Dealing with impurity 29-May-19.
CISC101 Reminders Assignment 3 due today.
CSE341: Programming Languages Lecture 12 Equivalence
PROGRAMMING IN HASKELL
Haskell Dealing with impurity 28-Jun-19.
CSE341: Programming Languages Lecture 12 Equivalence
Records and Type Classes
Presentation transcript:

PROGRAMMING IN HASKELL Functors Based on Haskell wikibook, the book “Learn You a Haskell for Great Good” (and a few other sources)

Functors Functors are a typeclass, just like Ord, Eq, Show, and all the others. This one is designed to hold things that can be mapped over; for example, lists are part of this typeclass. class Functor f where       fmap :: (a -> b) -> f a -> f b This type is interesting - not like previous exmaples, like in EQ, where (==) :: (Eq a) => a -> a -> Bool. Here, f is NOT a concrete type, but a type constructor that takes one parameter. 1

fmap :: (a -> b) -> f a -> f b Compare fmap to map: fmap :: (a -> b) -> f a -> f b map :: (a -> b) -> [a] -> [b]    So map is a lot like a functor! Here, map takes a function and a list of type a, and returns a list of type b. In fact, can define map in terms of fmap: instance Functor [] where       fmap = map    2

instance Functor [] where fmap = map Notice what we wrote: instance Functor [] where       fmap = map    We did NOT write “instance Functor [a] where…”, since f has to be a type constructor that takes one type. Here, [a] is already a concrete type, while [] is a type constructor that takes one type and can produce many types, like [Int], [String], [[Int]], etc. 3

instance Functor Maybe where fmap f (Just x) = Just (f x) Another example: instance Functor Maybe where       fmap f (Just x) = Just (f x)       fmap f Nothing = Nothing    Again, we did NOT write “instance Functor (Maybe m) where…”, since functor wants a type constructor. Mentally replace the f’s with Maybe, so fmap acts like (a -> b) -> Maybe a -> Maybe b. If we put (Maybe m), would have (a -> b) -> (Maybe m) a -> (Maybe m) b, which looks wrong. 4

ghci> fmap (++ " HEY GUYS IM INSIDE THE Using it: ghci> fmap (++ " HEY GUYS IM INSIDE THE  JUST") (Just "Something serious.")   Just "Something serious. HEY GUYS IM INSIDE THE JUST"   JUST") Nothing   Nothing   ghci> fmap (*2) (Just 200)   Just 400   ghci> fmap (*2) Nothing   Nothing      5

Back to trees, as an example to put it all together: Let’s make a binary search tree type. Need comparisons to make sense, so want the type to be in Eq. Also going to have it be Show and Read, so anything in the tree can be converted to a string and printed (just to make displaying easier). data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show,Read,Eq) 6

So we can specify a tree (just like with our expressions last week): Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 6 EmptyTree EmptyTree) This will let us code an insert function, just like in data structures. First step – a function to make a single node tree: singleton :: a -> Tree a   singleton x = Node x EmptyTree EmptyTree 7

Find: findInTree :: (Ord a) => a -> Tree a -> Bool   findInTree x EmptyTree = False   findInTree x (Node a left right)       | x == a = True       | x < a  = findInTree x left       | x > a  = findInTree x right     Note: This is a binary search tree, so can be efficient. If it were an “unordered” tree, like we saw last week, would need to search both left and right subtrees. 8

Now – let’s go code insert! treeInsert :: (Ord a) => a -> Tree a -> Tree a   treeInsert x EmptyTree =    treeInsert x (Node a left right)  =       9

My insert: treeInsert :: (Ord a) => a -> Tree a -> Tree a treeInsert x EmptyTree = singleton x   treeInsert x (Node a left right)        | x == a = Node x left right       | x < a  = Node a (treeInsert x left) right       | x > a  = Node a left (treeInsert x right)      10

ghci> let numsTree = foldr treeInsert EmptyTree nums An example run: ghci> let nums = [8,6,4,1,7,3,5]   ghci> let numsTree = foldr treeInsert EmptyTree nums   ghci> numsTree   Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 7 (Node 6 EmptyTree EmptyTree) (Node 8 EmptyTree EmptyTree))    11

(a -> b) -> Tree a -> Tree b Back to functors: If we looked at fmap as though it were only for trees, it would look something like: (a -> b) -> Tree a -> Tree b We can certainly phrase this as a functor, also: instance Functor Tree where       fmap f EmptyTree = EmptyTree       fmap f (Node x leftsub rightsub) =  Node (f x) (fmap f leftsub)  (fmap f rightsub)    12

Using the tree functor: ghci> fmap (*2) EmptyTree   EmptyTree   ghci> fmap (*4) (foldr treeInsert  EmptyTree [5,7,3,2,1,7])   Node 28 (Node 4 EmptyTree (Node 8 EmptyTree (Node 12 EmptyTree (Node 20 EmptyTree EmptyTree)))) EmptyTree     13

Another functor: IO actions main = do line <- getLine let line' = reverse line putStrLn $ "You said " ++ line' ++ " backwards!" putStrLn $ "Yes, you really said" ++ line' ++ " backwards!" In the above code, we are getting a line as an IO action, then reversing it and printing it back out. But – an IO is a functor, which means it is designed to be mapped over using fmap!

Another functor: IO actions Old way: main = do line <- getLine let line' = reverse line putStrLn $ "You said " ++ line' ++ " backwards!" putStrLn $ "Yes, you really said" ++ line' ++ " backwards!" Better way: use fmap! This getline has type IO String, so fmap with a string function will map the function over the result of getLine: main = do line <- fmap reverse getLine putStrLn $ "You said " ++ line ++ " backwards!" putStrLn $ "Yes, you really said" ++ line ++ " backwards!"

Functors and IOs In general, any time you are binding an IO action to a name, only to call functions on that name, use fmap instead! import Data.Char import Data.List main = do line <- fmap (intersperse '-' . reverse . map toUpper) getLine putStrLn line $ runhaskell fmapping_io.hs hello there E-R-E-H-T- -O-L-L-E-H

How do these type? ghci> :t fmap (*2) fmap (*2) :: (Num a, Functor f) => f a -> f a ghci> :t fmap (replicate 3) fmap (replicate 3) :: (Functor f) => f a -> f [a] The expression fmap (*2) is a function that takes a functor f over numbers and returns a functor over numbers. That functor can be a list, a Maybe , an Either String, whatever. The expression fmap (replicate 3) will take a functor over any type and return a functor over a list of elements of that type.

What will one of these do? ghci> fmap (replicate 3) [1,2,3,4] [[1,1,1],[2,2,2],[3,3,3],[4,4,4]] ghci> fmap (replicate 3) (Just 4) Just [4,4,4] ghci> fmap (replicate 3) Nothing Nothing The type fmap (replicate 3) :: (Functor f) => f a -> f [a] means that the function will work on any functor. What exactly it will do depends on which functor we use it on. If we use fmap (replicate 3) on a list, the list's implementation for fmap will be chosen, which is just map. If we use it on a Maybe a, it'll apply replicate 3 to the value inside the Just, or if it's Nothing, then it stays Nothing.

Functor laws There are two laws any functor MUST follow if you define them: fmap id = id fmap (g . f) = fmap g . fmap f If we can show that some type obeys both functor laws, we can rely on it having the same fundamental behaviors as other functors when it comes to mapping. We can know that when we use fmap on it, there won't be anything other than mapping going on behind the scenes and that it will act like a thing that can be mapped over, i.e. a functor. This leads to code that is more abstract and extensible, because we can use laws to reason about behaviors that any functor should have and make functions that operate reliably on any functor.

Takeaway: WHY? The availability of the fmap method relieves us from having to recall, read, and write a plethora of differently named mapping methods (maybeMap, treeMap, weirdMap, ad infinitum). As a consequence, code becomes both cleaner and easier to understand. On spotting a use of fmap, we instantly have a general idea of what is going on. Thanks to the guarantees given by the functor laws, this general idea is surprisingly precise. Using the type class system, we can write fmap-based algorithms which work out of the box with any functor - be it [], Maybe, Tree or whichever you need. Indeed, a number of useful classes in the core libraries inherit from Functor.