Skip to main content

Notes on Learn you a Haskell (Functors and Applicative Functors)

We're reading Learn You a Haskell for Great Good! at the office's non-fiction book club! This covers the first two parts of chapter 11 (Functors, Applicative Functors and Monoids) up to the end of the discussion on applicative functors.

Legend

  • Regular text summarizes a point the document made.
  • Italicized font expresses my terrible, terrible opinions or attempts at explaination.
  • Highlighted  text indicates that this note was important to my understanding, and if it's italicized, then I even thought of it myself!

Notes

  • Haskell's type system doesn't require placing types in a hierarchy, which can be useful for designs.
    • Typeclasses are "open" for extension
  • Recap: `Eq` (equateable) and `Functor` (mappable)
  • Functors
    • Recap
      • e.g. lists, Maybe, trees.
      • Typeclass fmap :: (a->b) -> f a -> f b
      • Functors are "computational context" e.g. that a value exists or not, that there are multiple values, etc.
        • "[They] output values in a context."
      • `fmap` only accepts type constructors w/kind * -> *
        • i.e. can't make instance of `Either`; needs to make an instance of `Either a`
      • IO and (->) r are also instances of functor.
    • instance Functor IO …
      • Mapping over an io action's output
      • The type of this fmap instance is fmap :: (a -> b) -> IO a -> IO b
      • Fmap can be useful for eliding a the result of a bound I/O action
    • (->) r
      • It's the function type r -> a, written in prefix notation.
      • (->) has the wrong kind, so it has to be partially applied to create an instance of fmap
      •  this type would be (using the previous definition of fmap, and substituting in the type constructor)  fmap :: (a->b) -> ((->) r a) -> ((->) r b)
      • This is just function composition, though! This is a clever-er way to write that instance:
    instance Functor ((->) r) where 
        fmap = (.) 
  • Re. the box analogy and function composition …
    • The box is the eventual result of (+100).
    • Alt. interp.: `fmap` is about intercepting when functions are applied. E.g. Applying a function to a `Maybe Int` means it needs to take a `Maybe Int` parameter; `fmap`'ing a function to a `Maybe Int` means it needs to take an `Int`. Similarly, applying a function to `Supplier<T>` means it needs to take a `Supplier<T>` … `fmap`'ing it allows the function to take an unwrapped `T`.
  • Functor over numbers = functor with numbers in it (if that helps)
  • Lifting a function = converting foo :: a -> b to foo :: (Functor f) => f a -> f b
    • Fmap can be thought of a function which takes another function and lifts it to work on functors.
    • Fmap on Either is weird -- didn't expect it to only operate on the Right value.
  • Functor laws
    • Aren't enforced by compiler; needs explicit testing.
    1. `fmap id = id`
      • `fmap`ing id over the contents of a Functor should be the same as id'ing a Functor.
      • Reminder: id just returns its parameter unmodified.
    2. `fmap (f . g) = fmap f . fmap g`
      • Composing two functions should give the same result as composing those two lifted functions.
    • Counterexample: incrementing a counter after each function application (e.g. breaks rule 1)
    • Laws are useful since they can be assumed to be true, and make the functions more easily reusable.
  • Applicative functors
    • They're beefed up functors :3
      • i.e. they add a couple extra functions on top of the functor typeclass
    • Normal functors: only permit mapping functions over existing functors of values. Not functors of functions of values.
    • Typeclass functions:
      • `pure`: wrap a value in an applicative functor
      • `<*>`: apply a wrapped value to another wrapped value
        • Note! The comparison to `fmap` really threw me for a loop! IMO it's easier to just treat it as its own thing, especially when you hit the implementation of <*> for functions.
    • Upshot: it permits partial application within a context (e.g. you can partially apply a function w/<*> then apply that to another applicative functor)
    • <?> is cute, but annoying.
      • Me from the future reporting; I've changed my mind.
    • The instance of applicative for [] of <*> applies each function in the left to each value on the right, in a cartesian product.
    • Hoo boy the filter greater than fifty and multiple the cartesian product of both lists together example is crazy.
    • Instance of applicative for IO uses `<-` to get the values out of its parameters and returns their application.
      • Thinking about this implementation requires talking about sequencing -- e.g. getLine causes side effects, so <*>'ing multiple IO's means they need to execute in a particular order.
      • If you need to bind IO actions then use them, try using <*> instead if it's more clear.
    • Function application implements Applicative, too.
      • `pure` is a function which ignores its first parameter and returns the wrapped value.
      • <*> is a function that returns a function which passes its parameter to the right applicative functor's value, then the left applicative functor, then applies the result of the left functor to the right's value.
        • … or f <*> g = \x -> f x (g x)
        • I prefer \x -> f x $ g x, weirdly enough.
          • It takes a function w/two parameters and a function w/one parameter and pipes the output of the right one and the original value into the left.
        • It might be useful to contrast this against the definition of `fmap` for (->) too.
          • `fmap f g = (\x -> f (g x)) `
      • `(+) <$> (+3) <*> (*100) $ 5` = 508
        • English: partially apply (+) and (+3) (:t a -> a -> a), apply that to 5 and the result of (5*100).
        •  
          Poorly illustrating (top) that input feeds into both functions, (bottom) the order of execution.
  • ZipList applicatives have the behaviour of … zipping … not <*>'ing the cartesian product.
    • `pure` for ZipList makes an infinite ZipList of 'x'
      • Didn't fully grasp how this is important for maintaining the law for `pure f <*> xs`. Come back to this later?
  • liftA2
    • "lifts" a function which takes two parameters into a function which takes two applicatives.
  • sequenceA
    • takes a list of functors and returns a functor of a list
    • The `foldr` + `liftA2` implementation is a lot nicer IMO?
    • Note: a sequenceA of a list of functions returns a function (because the function is a functor!)
  • Note: the author's using an unusual definition for "non-deterministic".
  • Laws:
    • `pure f <*> x = fmap f x`: <*>'ing a function to an applicative should be the same as `fmap`'ing it to a functor.
    • `pure id <*> v = v`: <*>'ing id to an applicative should just return that applicative.
    • `pure (.) <*> u <*> v <*> w = u <*> (v <*> w)`: composing the applicatives (rhs) should return the same thing as applying the composition operator to each applicative in turn.
    • `pure f <*> pure x = pure (f x)`: wrapping the output of a function application (rhs) should return the same thing as <*>'ing their applicatives together.
    • `u <*> pure y = pure ($ y) <*> u`: ??? (seems too self-evident?)
  • Applicatives are useful for combining computations using the applicative style.
    • … OK, hot take, but I don't like instance Applicative for anything other than functions and IO. Nothing <*> Just a returning Nothing is weird, and the whole cartesian product thing with lists is even weirder.

Comments

  1. How to make money from betting on football - Work Tomake Money
    If you're having problems finding a titanium flat iron winning bet หาเงินออนไลน์ online for the https://febcasino.com/review/merit-casino/ day of your 메이피로출장마사지 choosing, 바카라 then there are plenty of opportunities available right here.

    ReplyDelete
  2. Lie Yan Zuan Shi is a top-quality Asian Slot with five reels, 25 fastened paylines, and onerous and fast|a set} jackpot of 3,000 credit all the time out there when you play for the maximum. Depending on your location, 888casino has a free £88 no deposit bonus out there right on registration. Check out the location's phrases and conditions to seek out|to search out} out whether you should use|you must use} it or not. Inspired by Dan Brown's books, the Da Vinci's Vault Slot machine is an exhilarating recreation by Playtech with five reels and 20 paylines. When you play playing games online, you could have} only one assure, and that is that 카지노사이트 you'll not win every time you play.

    ReplyDelete

Post a Comment

Popular posts from this blog

Notes on Learn you a Haskell (Newtype and Monoids)

We're reading Learn You a Haskell for Great Good! at the office's non-fiction book club! This covers the second two parts o f chapter 11 (Functors, Applicative Functors and Monoids) — from the section entitled 'The newtype keyword' and onwards. Legend Regular text summarizes a point the document made. Italicize d font expresses my terrible, terrible opinions or attempts at explanation. Highlighted   text indicates that this note was important to my understanding, and if it's italicized, then I even thought of it myself! Notes The newtype keyword Recap Data = ADT Type = type synonym <*> can logically have different implementations for e.g. a list Such as ZipList vs [] Adding additional typeclass instances for a type Can be done by wrapping the existing type in a `data` with a single constructor. Use a record to make it easier to extract contents. Downside: slight perf overhead. Use newtype: wraps an existing ...