0 Functors in Haskell Adapted from material by Miran Lipovaca.

Slides:



Advertisements
Similar presentations
Why not just use Arrays? Java ArrayLists.
Advertisements

1 Functional Programming Lecture 8 - Binary Search Trees.
Functional Programming Lecture 10 - type checking.
Higher-Order Functions and Loops c. Kathi Fisler,
Haskell user defined types data Temp = Cold|Hot|Warm deriving (Show,Eq, Ord, Enum) -- to enable printing to screen -- comparing for equality -- comparison.
15-Jan-15 More Haskell Functions Maybe, Either, List, Set, Map.
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,
Haskell Chapter 5, Part I. Topics  Higher Order Functions  map, filter  Infinite lists Get out a piece of paper… we’ll be doing lots of tracing.
String is a synonym for the type [Char].
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.
Chapter 5 Polymorphic and Higher-Order Functions.
0 PROGRAMMING IN HASKELL Chapter 3 - Types and Classes.
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.
PrasadCS7761 Haskell Data Types/ADT/Modules Type/Class Hierarchy Lazy Functional Language.
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?
TECH Computer Science Data Abstraction and Basic Data Structures Improving efficiency by building better  Data Structure Object IN  Abstract Data Type.
Advanced Functional Programming 2009 Ulf Norell (lecture by Jean-Philippe Bernardy)
5 BASIC CONCEPTS OF ANY PROGRAMMING LANGUAGE Let’s get started …
Lee CSCE 314 TAMU 1 CSCE 314 Programming Languages Haskell: Types and Classes Dr. Hyunyoung Lee.
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.
What is a Type? A type is a name for a collection of related values. For example, in Haskell the basic type Bool contains the two logical values: True.
0 Odds and Ends in Haskell: Folding, I/O, and Functors Adapted from material by Miran Lipovaca.
Lee CSCE 314 TAMU 1 CSCE 314 Programming Languages Interactive Programs: I/O and Monads Dr. Hyunyoung Lee.
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.
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.
Haskell Chapter 5, Part II. Topics  Review/More Higher Order Functions  Lambda functions  Folds.
Defining Classes Modules and ADTs CSCE 314 Spring 2016.
Coding – Week 2 Functions, Arrays, and Objects. Functions  Functions are not a new concept – you’ve been using them already.  void setup() {} and void.
24-Jun-16 Haskell Dealing with impurity. Purity Haskell is a “pure” functional programming language Functions have no side effects Input/output is a side.
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.
Polymorphic Functions
Haskell Chapter 8.
Haskell Chapter 7.
String is a synonym for the type [Char].
Recursion.
PROGRAMMING IN HASKELL
Types CSCE 314 Spring 2016.
Theory of Computation Lecture 4: Programs and Computable Functions II
Binary search tree. Removing a node
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
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
Higher Order Functions
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
Type & Typeclass Syntax in function
Advanced Functional Programming
Types and Classes in Haskell
Haskell Types, Classes, and Functions, Currying, and Polymorphism
RECURSION Haskell.
Records and Type Classes
Grab Bag of Interesting Stuff
PROGRAMMING IN HASKELL
Programming Languages
PROGRAMMING IN HASKELL
Theory of Computation Lecture 4: Programs and Computable Functions II
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
Records and Type Classes
Presentation transcript:

0 Functors in Haskell Adapted from material by Miran Lipovaca

1 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 Only one typeclass method, called fmap. Basically, says: give me a function that takes a and returns b and a “box” with type a inside of it, and I’ll return a “box” with type b inside of it.

2 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

3 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.

4 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.

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

6 Another example - the tree class. We’ll define a tree to be either: - an empty tree - an element with a value and two (sub)trees data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq) This is slightly different than our last definition. Here, trees will be of the form: Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 6 EmptyTree EmptyTree)

7 Some tree functions: singleton :: a -> Tree a singleton x = Node x EmptyTree EmptyTree 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)

8 And now to find an element in the tree: treeElem :: (Ord a) => a -> Tree a -> Bool treeElem x EmptyTree = False treeElem x (Node a left right) | x == a = True | x < a = treeElem x left | x > a = treeElem x right Note: we deliberately assumed we could compare the elements of the nodes so that we could make this a binary search tree. If this is an “unordered” tree, would need to search both left and right subtrees.

9 An example run: ghci> let nums = [8,6,4,1,7,3,5] ghci> let numsTree = foldr treeInsert EmptyTre e nums ghci> numsTree Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (N ode 4 EmptyTree EmptyTree)) (Node 7 (Node 6 Em ptyTree EmptyTree) (Node 8 EmptyTree EmptyTre e))

10 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)

11 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 (N ode 12 EmptyTree (Node 20 EmptyTree EmptyTree )))) EmptyTree

12 Some rules: -The type has to have a kind of * -> *, which means it takes only 1 concrete type as a parameter. Example: ghci> :k Maybe Maybe :: * -> *

If a type takes 2 parameters, like Either: data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show) ghci> Right 20 Right 20 ghci> :t Right 'a' Right 'a' :: Either a Char ghci> :t Left True Left True :: Either Bool b

Then the signature :k looks like this: ghci> :k Either Either :: * -> * -> * For this situation, we need to partially apply the type constructor until it takes only 1 input. Remember, Haskell is good at partial evaluations! instance Functor (Either a) where fmap :: (b -> c) -> Either a b -> Either a c

Another functor: the <- in the IO class instance Functor IO where fmap f action = do result <- action return (f result) Comments: -The result of an IO action must be an IO action, so we’ll use do to glue our 2 things together. - We can use this to make our code more compact, since we won’t need to explicitly “unpack” the IO. - Now (for example) the command: fmap (++ “!”) getline Will behave just like getline, but with a ! at the end of the line

Example: program 1 main = do line <- getLine let line' = reverse line putStrLn $ "You said " ++ line' ++ " backwards!" With fmap: main = do line <- fmap reverse getLine putStrLn $ "You said " ++ line ++ " backwards!"

There are two rules that every functor should obey. Note that these aren’t enforced by Haskell, but they are important! First: If we map the identity function over a functor, then the functor we get back should be the same as the original functor. So: fmap id = id. ghci> fmap id (Just 3) Just 3 ghci> id (Just 3) Just 3 ghci> fmap id [1..5] [1,2,3,4,5] ghci> id [1..5] [1,2,3,4,5]

Second rule: composing two functions and then mapping the resulting function over a functor should be the same as first mapping one function over the functor and then mapping the other. So: fmap (f. g) = fmap f. fmap g. Huh? Well, fmap (f. g) (Just x) is (if you go back and look) defined as Just ((f. g) x), which is the same as Just (f (g x)), since we ’ re just composing functions. And fmap f (fmap g (Just x)) is fmap f (Just (g x)) which is then Just (f (g x)), so we ’ re ok!

Now why do we care? If we know that a type obeys both laws, we can make certain assumptions about how it will act. If a type obeys the functor laws, we know that calling fmap on a value of that type will only map the function over it, nothing more. This leads to code that is more abstract and extensible. All the Functor examples in Haskell obey these laws by default.