Skip to main content
deleted 2 characters in body
Source Link
Rufflewind
  • 2.2k
  • 2
  • 16
  • 19

The type constructor (->) r a can be thought of as r -> ___ where r is already known but ___ is to be filled in later. Its kind is * -> * (“type constructor”).

The type constructor (->) r a can be thought of as r -> ___ where r is already known but ___ is to be filled in later. Its kind is * -> * (“type constructor”).

The type constructor (->) r can be thought of as r -> ___ where r is already known but ___ is to be filled in later. Its kind is * -> * (“type constructor”).

Remove stray sentence.
Source Link
Rufflewind
  • 2.2k
  • 2
  • 16
  • 19

The type constructor (->) r a can be thought of as r -> ___ where r is already known but ___ is to be filled in later. Its kind is * -> * (“type constructor”). I’ll stick to `r

The <*> function takes two functions that expect to receive an Int later.:

The goal of <*> is to create a new function that produces some value b in exchange for an Int. There is one “obvious” way to play this game: when you do get that Int later in the future, you send it to both f and g and they will in exchange give you a -> b and a. Then you can apply the a -> b to a to get b, which completesachieves the tradegoal.

Compared to the previous examples, the get function is rather unusual in that it does actually look atinspect the Int argument, returning exactly what it sees. This means the result of this chain of computations differs depending on what argument it receives at the very end.

  pure (-) <*> get <*> pure 7
≡ (\ input\input -> (-)) <*> (\input -> input) <*> (\ input\input -> 7)
≡ (\input -> (-) input) <*> (\ _\input -> 7)
≡ (\input -> (-) input 7)
≡ (\input -> input - 7)

The type constructor (->) r a can be thought of as r -> ___ where r is already known but ___ is to be filled in later. Its kind is * -> * (“type constructor”). I’ll stick to `r

The <*> function takes two functions that expect to receive an Int later.

The goal of <*> is to create a new function that produces some value b in exchange for an Int. There is one “obvious” way to play this game: when you do get that Int later in the future, you send it to both f and g and they will in exchange give you a -> b and a. Then you can apply the a -> b to a to get b, which completes the trade.

Compared to the previous examples, the get function is rather unusual in that it does actually look at the Int argument, returning exactly what it sees. This means the result of this chain of computations differs depending on what argument it receives at the very end.

  pure (-) <*> get <*> pure 7
≡ (\ input -> (-)) <*> (\input -> input) <*> (\ input -> 7)
≡ (\input -> (-) input) <*> (\ _ -> 7)
≡ (\input -> (-) input 7)
≡ (\input -> input - 7)

The type constructor (->) r a can be thought of as r -> ___ where r is already known but ___ is to be filled in later. Its kind is * -> * (“type constructor”).

The <*> function takes two functions:

The goal of <*> is to create a new function that produces some value b in exchange for an Int. There is one “obvious” way to play this game: when you do get that Int later in the future, you send it to both f and g and they will in exchange give you a -> b and a. Then you can apply the a -> b to a to get b, which achieves the goal.

Compared to the previous examples, the get function is rather unusual in that it does actually inspect the Int argument, returning exactly what it sees. This means the result of this chain of computations differs depending on what argument it receives at the very end.

  pure (-) <*> get <*> pure 7
≡ (\input -> (-)) <*> (\input -> input) <*> (\input -> 7)
≡ (\input -> (-) input) <*> (\input -> 7)
≡ (\input -> (-) input 7)
≡ (\input -> input - 7)
Source Link
Rufflewind
  • 2.2k
  • 2
  • 16
  • 19

The type (->) r a is just an alias for r -> a, nothing more. Its kind is * (“type”).

The type constructor (->) r a can be thought of as r -> ___ where r is already known but ___ is to be filled in later. Its kind is * -> * (“type constructor”). I’ll stick to `r

The (->) r type constructor is itself is parametrized by the input type r. To reduce the risk confusion, it might help to fix r to something concrete. So for the rest of this answer, I’ll choose r = Int. The explanation below will work for any choice of r.

Both applicative functors and functors classify type constructors, not types:

  • We say a [___] (list constructor) is a functor, Int -> ___ (functions that expect an integer) is a functor, IO ___ is a functor, Maybe ___ is a functor, etc.
  • We do not say [Int] (list of integers) is a functor, nor do we say Int -> String (functions expect an integer and return a string) is a functor, nor IO (), nor Maybe String, etc. (Of course, in informal writing people do say this out of sloppiness, but while learning it’s important to keep this distinction in mind.)

pure :: a ->        f a     -- general type, where f is Applicative
pure :: a -> (->) Int a     -- specialized type for f = ((->) Int)
pure :: a ->  (Int -> a)    -- just another way to write the above
pure x = (\_ -> x)

The pure function creates a function that will return a, ignoring whatever Int it will receive. It provides a way to “wrap” things into an applicative functor.

As a concrete example, if you write pure "hi", this creates a function \_ -> "hi" that always returns "hi" no matter what input it gets. To get your "hi" back, you apply the result to any integer, “unwrapping” the applicative functor:

>>> (pure "hi") 42
"hi"

(<*>) ::        f (a -> b)  ->        f a  ->        f b     -- general type, where f is Applicative
(<*>) :: (->) Int (a -> b)  -> (->) Int a  -> (->) Int b     -- specialized type for f = ((->) Int)
(<*>) ::  (Int -> (a -> b)) ->  (Int -> a) ->  (Int -> b)    -- just another way to write the above
f <*> g = \x -> f x (g x)

The <*> function takes two functions that expect to receive an Int later.

  • The first function f will return a function a -> b in exchange for an Int.
  • The second function g will return some value a in exchange for an Int.

The goal of <*> is to create a new function that produces some value b in exchange for an Int. There is one “obvious” way to play this game: when you do get that Int later in the future, you send it to both f and g and they will in exchange give you a -> b and a. Then you can apply the a -> b to a to get b, which completes the trade.

Here’s a concrete example: pure (+) <*> pure 2.0 <*> pure 3.0. This expression creates a function that always yields 5.0 in exchange for whatever Int you give. As usual you can “unwrap” it by applying it to some Int:

>>> (pure (+) <*> pure 2.0 <*> pure 3.0) 42
5.0

But so far, none of these examples were very interesting, because they only “wrap” things using pure, which always ignores the argument. Here’s a more interesting one:

>>> let get input = input
>>> (pure (-) <*> get <*> pure 7) 42
35
>>> (pure (-) <*> get <*> pure 7) 10
3

Compared to the previous examples, the get function is rather unusual in that it does actually look at the Int argument, returning exactly what it sees. This means the result of this chain of computations differs depending on what argument it receives at the very end.

  pure (-) <*> get <*> pure 7
≡ (\ input -> (-)) <*> (\input -> input) <*> (\ input -> 7)
≡ (\input -> (-) input) <*> (\ _ -> 7)
≡ (\input -> (-) input 7)
≡ (\input -> input - 7)

In fact, if you just look at the original applicative expression and squint a bit to ignore the pure and <*>, you can read it directly as

  (-) INPUT 7
≡ INPUT - 7

where INPUT represents the future Int value.

Now consider your example:

  (+) <$> (*2) <*> (+7)
≡ (+) <$> (\input -> input * 2) <*> (\input -> input + 7)
≡ (\input -> (+) (input * 2)) <*> (\input -> input + 7)
≡ (\input -> (+) (input * 2) (input + 7))
≡ (\input -> (input * 2) + (input + 7))

Informally this describes the following computation:

  (+) (INPUT * 2) (INPUT + 7)
≡ (INPUT * 2) + (INPUT + 7)

For example, if the input is 42, you should expect 133 as the result:

>>> ((+) <$> (*2) <*> (+7)) 42
133

The purpose of the ((->) Int) applicative functor to allow the programmer to separate the logic that depends on the input argument from the actual code that supplies the concrete value. In other words, in real code you would likely be building a rather large and complicated applicative expression, without knowing what the input will be. You would not be unwrapping the applicative value right away, but likely some far-away place in the code (perhaps in another project entirely!).