...
 
Commits (6)
-- Functor: a typeclass representing
-- "types that can be mapped over."
-- Functor: one of the most fundamental typeclasses
-- in standard library. It represents a "container"
-- as well as an ability to apply functions to it.
-- List, Map, Tree are instances of Functor.
-- f is a type _function_, * -> *
class MyFunctor f where
-- f is a container
class Functor f where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
(<$) :: a -> f b -> f a
(<$) :: fmap . const
instance Functor [] where
fmap :: (a -> b) -> [a] -> [b]
fmap _ [] = []
fmap g (x:xs) = g x : fmap g xs
-- alternatively: fmap = map.
instance Functor Maybe where
fmap :: (a -> b) -> Maybe a -> Maybe b
fmap _ Nothing = Nothing
fmap g (Just a) = Just (g a)
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show)
instance Functor Tree where
fmap f (Leaf x) = Leaf (f x)
fmap f (Branch left right) = Branch (fmap f left) (fmap f right)
-- NonEmpty a
-- fmap (+2) [1..10]
-- fmap (+2) Just 2
-- fmap (+2) Nothing
-- fmap (2*) (Branch (Branch (Leaf 1) (Leaf 2)) (Branch (Leaf 3) (Leaf 4)))
-- Kind mismatch error!
-- instance Functor Int where fmap = map
-- Laws
-- Sort of like a group "homomorphism"
-- fmap id = id
-- fmap (g . h) = (fmap g) . (fmap h)
--
-- ghci> (fmap even . fmap length) (Just "twelve")
-- Just True
-- ghci> fmap (even . length) (Just "twelve")
-- Just True
-- Curry intuition
-- Does not really take two parameters, but takes a single
-- parameter and returns a function.
-- fmap :: (a -> b) -> (f a -> f b)
-- fmap "lifts" a function from the normal world into the "f" world.
--
-- Lifting (will come back with Monads)
-- This an important example.
-- What is fmap over functions?
-- In Control.Monad.Instances
instance Functor ((->) r) where
fmap f g = (\x -> f (g x))
-- OR
instance Functor ((->) r) where
fmap = (.)
--
-- :m + Control.Monad.Instances
-- :t fmap (*3) (+100)
-- fmap (*3) (+100) 1
-- fmap :: (a -> b) -> (->) r a -> (->) r b
-- OR
-- fmap :: (a -> b) -> (r -> a) -> (r -> b)
-- A value of type (e -> a) is an r-indexed container, with one value
-- of a for each value of r.
-- Mapping a function over every value corresponds to function copmposition.
-- We first apply r-> a function to pick aut an a from the original container
-- then apply the a -> b function to transform the element we picked.
--
-- Either e a is a container that can either contain a value of type a or a value of type e.
-- intro to containers. From https://haskell-containers.readthedocs.io/en/latest/index.html
-- Data.Map.Strict; Data.Map.Lazy; Data.IntMap
-- import qualified Data.Map.Strict as Map
-- Strict version: not lazy
-- :t Map.fromList
-- Keys need to be in the Ord typeclass.
-- let m1 = Map.fromList [("a", 1), ("b", 2)]
-- let m2 = Map.delete "a" m1
-- All implementations are immutable. Any update functions create a new map.
--
-- nums = Map.fromList [(1, "one"), (2, "two"), (3, "three")]
-- Map.lookup 3 nums
-- Map.lookup 4 nums
--
-- let moreNums = Map.insert 4 "four" nums
-- Map.member 4 moreNums
--
-- Intro to sequences.
--
-- Lists:
-- Lists have O(1) cons and pattern matching.
-- Similar to generators in Python (which are also lazy).
-- !!, ++ is O(N)
--
-- Sequences work very similar to lists, with a few key differences.
-- Functions are similar.
-- Sequences based on finger trees.
-- O(1) access to front and rear with <|, |>, viewl, viewr.
-- O(log N) concatenation with ><
-- O(log N) splitting with splitAt, take, and drop
-- O(log N) access to any elemeent with lookup, !?, etc.
-- Performance notes: https://wiki.haskell.org/Performance
-- import Data.Sequence (Seq(..), (<|), (|>), (<>))
-- import qualified Data.Sequence as Seq
-- let nums = Seq.fromList [1,2,3]
-- 0 <| nums
-- nums |> 4
-- Seq.reverse (Seq.fromList [0, 1, 2])
-- Seq.fromList [-2, -1] >< nums
-- Seq.length nums
-- Seq.lookup 2 nums vs. Seq.index 2 nums
module Main where
main :: IO ()
main = do
putStrLn "hello world"
......@@ -44,8 +44,7 @@ this page will be updated throughout the course with new material.
Monoid](notes/Semigroup_and_Monoid.html)-->: Abstracting compositional
structures.
1. Functor and Applicative<!--[Functor and Applicative](notes/Functor.html)-->:
Lifting function application to a new level.
1. [Functor](notes/Functor.html): Lifting function application to a new level.
1. Monad<!--[Monad](notes/Monad.html)-->: It isn't *that* complicated.
......
---
title: Functor
---
## Functor<label for="functor" class="margin-toggle sidenote-number"></label>
<input type="checkbox" id="functor" class="margin-toggle"/>
<span class="sidenote">
Functor on the [Typeclassopedia](https://wiki.haskell.org/Typeclassopedia#Functor).
</span>
The `Functor` is the most fundamental typeclass in the standard libraries. Intuitively, a `Functor` can be viewed as a sort of "container," coupled with an ability to apply a function to every element in the container. One example is a list: this is a container of elements, and we can uniformly apply a function to every element using `map`.
## Definition
```haskell
class Functor f where
fmap :: (a -> b) -> f a -> f b
-- Replace all locations in the input with
-- the same value. This may be
-- overridden with a more efficient version.
(<$) :: a -> f b -> f a
(<$) = fmap . const
```
Having a single `fmap` method means that we don't have to implement and remember several `map` methods for different data structures e.g. `map` over lists, `treeMap`, `maybeMap`.
This also enables us to write code that works with any `Functor`, simply by invoking `fmap` polymorphically. This enables a powerful sort of code reuse.
## Intuition
There are two main intuitions for `Functor`.
1) A `Functor` is a container, or more precisely, a _computational context_ we can map over. Data structures are the most natural example of this.
2) Since `fmap` is curried, we can write the type signature as `fmap :: (a -> b) -> (f a -> f b)`. It transforms a "normal" function `g :: a -> b` into one that operates over containers `fmap g :: f a -> f b`. This transformation is called a _lift_.
## The `[]` instance
Recalling the familiar pattern of mapping over a list, we can implement an instance of `Functor` as follows.
```haskell
instance Functor [] where
fmap :: (a -> b) -> [a] -> [b]
fmap g (x:xs) = g x : fmap g xs
-- Alternatively, fmap = map works here.
```
As we'd expect, `fmap` works like `map`:
```
ghci> fmap (\x -> x + 2) [1..10]
[3,4,5,6,7,8,9,10,11,12]
ghci> fmap (*2) [1..10]
[2,4,6,8,10,12,14,16,18,20]
```
## The `Maybe` instance
Similarly, `Maybe` is an instance of functor:
```haskell
instance Functor Maybe where
fmap :: (a -> b) -> Maybe a -> Maybe b
fmap _ Nothing = Nothing
fmap g (Just a) = Just (g a)
```
## The `Tree` instance
Suppose we have a `Tree` data structure defined recursively as follows:
```haskell
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show)
```
We can write a `Functor` instance as follows:
```haskell
instance Functor Tree where
fmap f (Leaf x) = Leaf (f x)
fmap f (Branch left right) = Branch (fmap f left) (fmap f right)
```
This gives us a function that operates as follows:
```haskell
*Main> x
Branch (Leaf 1) (Leaf 2)
*Main> :t x
x :: Num a => Tree a
*Main> fmap (+2) x
Branch (Leaf 3) (Leaf 4)
```
## Laws
There are two laws any `Functor` instance must satisfy:
1) `fmap id = id`
This just means mapping `id` over a container must leave the container unchanged.
2) `fmap (g . f) = fmap g . fmap f`
This says that it doesn't matter whether we map a composed function or first map one and then the other.
## Applicative<label for="applicative" class="margin-toggle sidenote-number"></label>
<input type="checkbox" id="applicative" class="margin-toggle"/>
<span class="sidenote">
Applicative on the [Typeclassopedia](https://wiki.haskell.org/Typeclassopedia#Applicative).
</span>
## Further references
- https://en.wikibooks.org/wiki/Haskell/The_Functor_class
---
title: Functor and Applicative
---
Intro stuff.
## Functor<label for="functor" class="margin-toggle sidenote-number"></label>
<input type="checkbox" id="functor" class="margin-toggle"/>
<span class="sidenote">
Functor on the [Typeclassopedia](https://wiki.haskell.org/Typeclassopedia#Functor).
</span>
The `Functor` is the most fundamental typeclass in the standard libraries. Intuitively, a `Functor` can be viewed as a sort of "container," coupled with an ability to apply a function to every element in the container. One example is a list: this is a container of elements, and we can uniformly apply a function to every element using `map`.
## Definition
```haskell
class Functor f where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
(<$) = fmap . const
```
## Applicative<label for="applicative" class="margin-toggle sidenote-number"></label>
<input type="checkbox" id="applicative" class="margin-toggle"/>
<span class="sidenote">
Applicative on the [Typeclassopedia](https://wiki.haskell.org/Typeclassopedia#Applicative).
</span>