I don't understand what "lifting" is. Should I first understand monads before understanding what a "lift" is? (I'm completely ignorant about monads, too :) Or can someone explain it to me with simple words?
Lifting is more of a design pattern than a mathematical concept (although I expect someone around here will now refute me by showing how lifts are a category or something).
Typically you have some data type with a parameter. Something like
data Foo a = Foo { ...stuff here ...}
Suppose you find that a lot of uses of Foo take numeric types (Int, Double etc) and you keep having to write code that unwraps these numbers, adds or multiplies them, and then wraps them back up. You can short-circuit this by writing the unwrap-and-wrap code once. This function is traditionally called a "lift" because it looks like this:
liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c
In other words you have a function which takes a two-argument function (such as the (+) operator) and turns it into the equivalent function for Foos.
So now you can write
addFoo = liftFoo2 (+)
Edit: more information
You can of course have liftFoo3, liftFoo4 and so on. However this is often not necessary.
Start with the observation
liftFoo1 :: (a -> b) -> Foo a -> Foo b
But that is exactly the same as fmap. So rather than liftFoo1 you would write
instance Functor Foo where
fmap f foo = ...
If you really want complete regularity you can then say
liftFoo1 = fmap
If you can make Foo into a functor, perhaps you can make it an applicative functor. In fact, if you can write liftFoo2 then the applicative instance looks like this:
import Control.Applicative
instance Applicative Foo where
pure x = Foo $ ... -- Wrap 'x' inside a Foo.
(<*>) = liftFoo2 ($)
The (<*>) operator for Foo has the type
(<*>) :: Foo (a -> b) -> Foo a -> Foo b
It applies the wrapped function to the wrapped value. So if you can implement liftFoo2 then you can write this in terms of it. Or you can implement it directly and not bother with liftFoo2, because the Control.Applicative module includes
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
and likewise there are liftA and liftA3. But you don't actually use them very often because there is another operator
(<$>) = fmap
This lets you write:
result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4
The term myFunction <$> arg1 returns a new function wrapped in Foo:
ghci> :type myFunction
a -> b -> c -> d
ghci> :type myFunction <$> Foo 3
Foo (b -> c -> d)
This in turn can be applied to the next argument using (<*>), and so on. So now instead of having a lift function for every arity, you just have a daisy chain of applicatives, like this:
ghci> :type myFunction <$> Foo 3 <*> Foo 4
Foo (c -> d)
ghci: :type myFunction <$> Foo 3 <*> Foo 4 <*> Foo 5
Foo d
Paul's and yairchu's are both good explanations.
I'd like to add that the function being lifted can have an arbitrary number of arguments and that they don't have to be of the same type. For example, you could also define a liftFoo1:
liftFoo1 :: (a -> b) -> Foo a -> Foo b
In general, the lifting of functions that take 1 argument is captured in the type class Functor, and the lifting operation is called fmap:
fmap :: Functor f => (a -> b) -> f a -> f b
Note the similarity with liftFoo1's type. In fact, if you have liftFoo1, you can make Foo an instance of Functor:
instance Functor Foo where
fmap = liftFoo1
Furthermore, the generalization of lifting to an arbitrary number of arguments is called applicative style. Don't bother diving into this until you grasp the lifting of functions with a fixed number of arguments. But when you do, Learn you a Haskell has a good chapter on this. The Typeclassopedia is another good document that describes Functor and Applicative (as well as other type classes; scroll down to the right chapter in that document).
Hope this helps!
Let's start with an example (some white space is added for clearer presentation):
> import Control.Applicative
> replicate 3 'a'
"aaa"
> :t replicate
replicate :: Int -> b -> [b]
> :t liftA2
liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c)
> :t liftA2 replicate
liftA2 replicate :: (Applicative f) => f Int -> f b -> f [b]
> (liftA2 replicate) [1,2,3] ['a','b','c']
["a","b","c","aa","bb","cc","aaa","bbb","ccc"]
> ['a','b','c']
"abc"
liftA2 transforms a function of plain types to a function of same types wrapped in an Applicative, such as lists, IO, etc.
Another common lift is lift from Control.Monad.Trans. It transforms a monadic action of one monad to an action of a transformed monad.
In general, "lift" lifts a function/action into a "wrapped" type (so the original function gets to work "under the wraps").
The best way to understand this, and monads etc., and to understand why they are useful, is probably to code and use it. If there's anything you coded previously that you suspect can benefit from this (i.e. this will make that code shorter, etc.), just try it out and you'll easily grasp the concept.
I wanted to write an answer because I wanted to write it from a different point of view.
Let's say you have a functor like for example Just 4 and you wanted to apply a function on that functor for example (*2). So you try something like this:
main = print $ (*2) (Just 4)
And you'll get an error:
No instance for (Num (Maybe a0)) arising from an operator section
• In the expression: * 2
Ok that fails. To make (*2) work with Just 4 you can lift it with the help of fmap.
The type signature of fmap:
fmap :: (a -> b) -> f a -> f b
Link:
https://hackage.haskell.org/package/base-4.16.0.0/docs/Prelude.html#v:fmap
The fmap function takes an a -> b function, and a functor. It applies the a -> b function to the functor value to produce f b.
In other words, the a -> b function is being made compatible with the functor. The act of transforming a function to make it compatible is lifting.
In o.o.p. terms this is called an adapter.
So fmap is a lifting function.
main = print $ fmap (*2) (Just 4)
This produces:
Just 8
According to this shiny tutorial, a functor is some container (like Maybe<a>, List<a> or Tree<a> that can store elements of some another type, a). I have used Java generics notation, <a>, for element type a and think of the elements as berries on the tree Tree<a>. There is a function fmap, which takes an element conversion function, a->b and container functor<a>. It applies a->b to every element of the container effectively converting it into functor<b>. When only first argument is supplied, a->b, fmap waits for the functor<a>. That is, supplying a->b alone turns this element-level function into the function functor<a> -> functor<b> that operates over containers. This is called lifting of the function. Because the container is also called a functor, the Functors rather than Monads are a prerequisite for the lifting. Monads are sort of "parallel" to lifting. Both rely on the Functor notion and do f<a> -> f<b>. The difference is that lifting uses a->b for the conversion whereas Monad requires the user to define a -> f<b>.
Related
I am learning about Applicative Functors and the pure function has the following type declaration:
pure :: a -> f a
I understand that the pure function takes a value of any type and returns an applicative value with that value inside it. So if the applicative instance was Maybe, pure 3 would give Just 3.
However, what happens when you apply pure to a value that's already inside an applicative value? E.g. what happens if you do something like pure Just 3?
What happens when you apply pure to a value that's already inside an applicative value?
It just gets wrapped in an additional layer. The end result is an applicative value nested inside another applicative value.
E.g. what happens if you do something like pure Just 3?
This is an interesting question, although probably not for the reasons you meant. The important point here is to distinguish pure (Just 3) — which was probably what you meant — from pure Just 3 = (pure Just) 3, which is what you wrote. This gives two scenarios:
pure (Just 3) simply applies pure to the value Just 3, which — as I discussed above — gives Just (Just 3), which is a nested applicative value.
(pure Just) 3 is an interesting case. Recall the types of pure and Just:
pure :: Applicative f => a -> f a
Just :: a -> Maybe a
-- so:
pure Just :: Applicative f => f (a -> Maybe a)
In other words, pure Just takes the function Just and wraps it inside an applicative value.
Next, we want to take pure Just and apply it to a value 3. But we can’t do this, since f (a -> Maybe a) is a value, not a function! So (pure Just) 3 should result in a type error.
…except it turns out to typecheck just fine! So we’re missing something. In this case, it turns out there is an applicative instance for functions:
instance Applicative ((->) r) where
pure x = \r -> x
(<*>) = _irrelevant_here
The syntax is a bit funny, but it basically means that r -> ... is an applicative. This particular instance is known as the Reader monad, and it’s very widely used. (For more about this particular data type, see e.g. here or here.) The idea is that r -> a can compute an a given an r input; in this case, pure x creates a function which ignores its input and returns x always, and f <*> x feeds the r input into both f and x, then combines the two. In this case, we’re only using pure, so it’s easy to evaluate (pure Just) 3 by hand:
(pure Just) 3
= (\r -> Just) 3
= Just
So the idea here is that pure wraps Just in an applicative value, which in this case happens to be a function; then, we apply this function to 3, which gets rid of the wrapper to reveal the original Just.
First of all, pure has the type:
pure :: Applicative f => a -> f a
To make things simpler, think of the kind of f
:k f
f :: * -> *
and the kind of a is *
then the type of a, is just a, any a, the most polymorphic of all (but with kind * remember). So you don't really care the value of a, you just have a restriction, and that's the typeclass Applicative, and the kind of f (remember * -> *)
so in this case:
gchi> pure 3 :: Maybe Int
ghci> Just 3
here f is Maybe and a is 3
In the same way
gchi> pure $ Just 3 :: Maybe (Maybe Int)
gchi> Just (Just 3)
here f is again Maybe and a is Just 3
and you can play a little changing the type to pure:
gchi> pure 3 :: [Double]
ghci> [3.0]
here, f is [], and a is 3
same way
ghci> pure [3] :: [[Double]]
ghci> [[3.0]]
finally here, f again, is [] and a is [3]
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
From my understanding, it takes a function f, where another function (a -> b) as its argument, returns a function f. Applying f to a then returns a function f and apply f to b.
Here is an example:
Prelude> (+) <$> Just 2 <*> Just 3
Just 5
But I don't quite understand how it works.
I guess (+) should be f, Just 2 and Just 3 should be a and b respectively. Then what is (a -> b)?
From my understanding, it takes a function f...
Unfortunately this is incorrect. In this case, f is a type, not a function. Specifically, f is a "higher-kinded type" with kind * -> *. The type f is the functor.
In this case, f is Maybe. So we can rewrite the function types, specializing them for Maybe:
pure :: a -> Maybe a
(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
It starts to become a bit clearer once you get this far. There are a couple different possible definitions for pure, but only one that makes sense:
pure = Just
The operator x <$> y is the same as pure x <*> y, so if you write out:
(+) <$> Just 2 <*> Just 3
Then we can rewrite it as:
pure (+) <*> pure 2 <*> pure 3
Although this technically has a more general type. Working with the functor laws, we know that pure x <*> pure y is the same as pure (x y), so we get
pure ((+) 2) <*> pure 3
pure ((+) 2 3)
pure (2 + 3)
In this case, we a and b are the types but since <*> appears twice they actually have different types in each case.
In the first <*>, a is Int and b is Int -> Int.
In the second <*>, both a and b are Int. (Technically you get generic versions of Int but that's not really important to the question.)
Applicative functors were introduced to Haskell as applicative style programming "Idioms". Unpacking this phrase, we have "applicative style programming"; which is just application of functions to arguments. We also have "idioms", or phrases in a language which have a special meaning. For example "raining cats and dogs" is an idiom for raining very heavily. Putting them together, applicative functors are function applications with special meaning.
Take for example, following Dietrich Epp's lead, anApplication defined by a function,
anApplication = f a
where
f = (+2)
a = 3
and, anIdiomaticApplication, defined with idiomatic application,
anIdiomaticApplication = f <*> a
where
f = Just (+2)
a = Just 3
The top level structure of these definitions are similar. The difference? The first has a space--normal function application--and the second has <*>--idiomatic function application. This illustrates how <*> facilitates applicative style: just use <*> in place of a space.
The application, <*>, is idiomatic because it carries a meaning other than just pure function application. By way of exposition, in anIdiomaticApplication we have something like this:
f <*> a :: Maybe (Int -> Int) <*> Maybe Int
Here, the <*> in the type is used to represent a type level function* that corresponds to the signature of the real <*>. To the type-<*> we apply the type arguments for f and a (respectively Maybe (Int -> Int) and Maybe Int). After application we have
f <*> a :: Maybe Int
As an intermediate step, we can imagine something like
f <*> a :: Maybe ((Int -> Int) _ Int)
With _ being the type level stand-in for regular function application.
At this point we can finally see the idiom-ness called out. f <*> a is like a normal function application, (Int -> Int) _ Int, in the Maybe context/idiom. So, <*> is just function application that happens within a certain context.
In parting, I'll emphasize that understanding <*> is only partially understanding its use. We can understand that f <*> a is just function application which some extra idiomatic meaning. Due to the Applicative laws, we can also assume that idiomatic application will be somehow sensible.
Don't be surprised, however, if you look at <*> and get confused since there is so little there. We must also be versed in the various Haskell Idioms. For instance, in the Maybe idiom either the function or value may not be present, in which case the output will be Nothing. There are of course, many others, but getting familiar with just Either a and State s should model a wide variety of the different kinds.
*Something like this could actually be made with a closed type family (untested)
type family IdmApp f a where
IdmApp (f (a->b)) a = f b
The type of map is: (a->b) -> [a] -> [b]
while the type of the functor fmap is:
Functor f => (a+b) -> f a -> f b
I read on wikipedia that map was a polymorphic morphism while fmap was a polytypic morphism but that doesn't really clear up things for me.
So my question is: is the map function a functor?
In Haskell terms, fmap is a method in the typeclass Functor, not the functor itself. [], Maybe, ... are type constructors which instantiates the class Functor and, abusing the language, you can say that "Maybe is a functor".
In mathematical terms, a functor (or more specifically in this case, an endofunctor in the category Hask, the category of Haskell types) is composed of two mappings: the first one from a type to another and the second from an arrow (a -> b) to another (f a -> f b) which preserves the structure. In that sense, Maybe is the first arrow which maps a type to another, say Int to Maybe Int and the fmap for Maybe is the second arrow.
No, but any container that implements fmap is a functor. Lists implement fmap as well (give it a try!), so lists are functors. Map is just the implementation of fmap for lists.
Like all (->) r types map is also a functor.
(->) r is actually just a simple function type like r -> a and all functions are functors. Like other functors you may think a function like a container but you get the contained value when you apply a value. However as for a Functor instance we can not have a type with two type variables. That's why we partially apply it. Just like Either a b is done like instance Functor (Either a). Since we are interested in the return value of a function the Functor instance of a function type is a partially applied r -> a which is (->) r.
So lets see a functions Functor instance.
instance Functor ((->) r) where
fmap f g = (\x -> f (g x))
So coming back to the question, map is a function with type (a -> b) -> [a] -> [b] and we can rewrite this like (a -> b) -> ([a] -> [b]) so here r variable in the Functor instance stands for a -> b and a variable in the Functor instance stands for [a] -> [b]. So lets apply fmap on map like fmap ($) map. Which essentially means it will return us a function like \x -> ($) map x. Where x has to be a (a -> b) type function. So lets use <$>; the infix representation of fmap in the below example ;
Prelude> (($) <$> map) (+2) [1,2,3]
[3,4,5]
So.. yes map is a functor.
Based on this question, in this code
data Promise a b = Pending (a -> b) | Resolved b | Broken
instance Functor (Promise x) where
fmap f (Pending g) = Pending (f . g)
IF
g :: a -> b
then
Pending g :: Promise a b
also
f :: b -> c
because of the existence of f . g.
That means
Pending (f . g) :: Promise a c`.
Wrapping up
fmap :: (b -> c) -> Promise a b -> Promise a c
Now fmap alone has this signature (adapted to the above)
fmap :: Functor f => (b -> c) -> f b -> f c
This only conforms if you assume that f = Promise a. While the end product seems reasonable, how do you interpret the type of f or equivalently what it the type of a partially applied promise Promise a?
At the type level you have another programming language, almost-Haskell. In particular, you can view types as having constructors and being able to be partially applied.
To view this a bit more rigorously, we introduce "types of types" called "kinds". For instance, the type constructor Int has kind
Int ::: *
where I write (:::) to read "has kind", though this isn't valid Haskell syntax. Now we also have "partially applied type constructors" like
Maybe ::: * -> *
which has a function type just like you'd expect at the value level.
There's one really important concept to the notion of kinds—values may instantiate types only if they are kind *. Or, for example, there exist no values of type Maybe
x :: Maybe
x = -- .... what!
In fact, it's not possible to even express a type of kind other than * anywhere where we'd expect that type to be describing a value.
This leads to a certain kind of restriction in the power of "type level functions" in Haskell in that we can't just universally pass around "unapplied type constructors" since they don't always make much sense. Instead, the whole system is designed such that only sensible types can ever be constructed.
But one place where these "higher kinded types" are allowed to be expressed is in typeclass definitions.
If we enable KindSignatures then we can write the kinds of our types directly. One place this shows up is in class definitions. Here's Show
class Show (a :: *) where
show :: a -> String
...
This is totally natural as the occurrences of the type a in the signatures of the methods of Show are of values.
But of course, as you've noted here, Functor is different. If we write out its kind signature we see why
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
This is a really novel kind of polymorphism, higher-kinded polymorphism, so it takes a minute to get your head all the way around it. What's important to note however is that f only appears in the methods of Functor being applied to some other types a and b. In particular, a class like this would be rejected
class Nope (f :: * -> *) where
nope :: f -> String
because we told the system that f has kind (* -> *) but we used it as though it could instantiate values, as though it were kind *.
Normally, we don't have to use KindSignatures because Haskell can infer the signatures directly. For instance, we could (and in fact do) write
class Functor f where
fmap :: (a -> b) -> f a -> f b
and Haskell infers that the kind of f must be (* -> *) because it appears applied to a and b. Likewise, we can fail "kind checking" in the same was as we fail type checking if we write something inconsistent. For instance
class NopeNope f where
fmap :: f -> f a -> a
implies that f has kind * and (* -> *) which is inconsistent.
You are only missing the equations for Resolved and Broken. The only reasonable implementation I can think of is
fmap f (Resolved x) = Resolved (f x)
fmap _ Broken = Broken
Other than that, your code is fine.
I wanted to add to #J. Abrahamson's fantastic answer. A lot of my understanding from Haskell's kind system is from this diogo castro's blog which I highly recommend.
Coming to the question of Partially Applied Types. Apart from type classes it's also possible to use them in type constructors. Taking an example from the blog.
data NonEmpty f a = MkNonEmpty { head :: a, tail :: f a }
:k NonEmpty
-- NonEmpty :: (* -> *) -> * -> *
:t MkNonEmpty { head = (3 :: Int), tail = Maybe 3 }
-- :: NonEmpty Maybe Int
This is an old question so this might be a recent addition to Haskell. It can be summarized as type constructors can take types and type constructors as arguments.
isAlphaNum :: Char -> Bool
isAlphaNum = (||) <$> isAlpha <*> isNum
I can see that it works, but I don't understand where the instances of Applicative (or Functor) come from.
This is the Applicative instance for ((->) r), functions from a common type. It combines functions with the same first argument type into a single function by duplicating a single argument to use for all of them. (<$>) is function composition, pure is const, and here's what (<*>) translates to:
s :: (r -> a -> b) -> (r -> a) -> r -> b
s f g x = f x (g x)
This function is perhaps better known as the S combinator.
The ((->) r) functor is also the Reader monad, where the shared argument is the "environment" value, e.g.:
newtype Reader r a = Reader (r -> a)
I wouldn't say it's common to do this for the sake of making functions point-free, but in some cases it can actually improve clarity once you're used to the idiom. The example you gave, for instance, I can read very easily as meaning "is a character a letter or number".
You get instances of what are called static arrows (see "Applicative Programming with Effects" by Conor McBride et al.) for free from the Control.Applicative package. So, any source type, in your case Char, gives rise to an Applicative instance where any other type a is mapped to the type Char -> a.
When you combine any of these, say apply a function f :: Char -> a -> b to a value x :: Char -> a, the semantic is that you create a new function Char -> b, which will feed its argument into both f and x like so,
f <*> x = \c -> (f c) (x c)
Hence, as you point out, this makes your example equivalent to
isAlphaNum c = (isAlpha c) || (isNum c)
In my opinion, such effort is not always necessary, and it would look nicer if Haskell had better syntactic support for applicatives (maybe something like 2-level languages).
It should be noted that you get a similar effect by using the lift functions, e.g.:
import Data.Char
import Control.Applicative
isAlphaNum = liftA2 (||) isAlpha isNumber
Or, using the monad instance of ((->) r) instead of the applicative one:
import Data.Char
import Control.Monad
isAlphaNum = liftM2 (||) isAlpha isNumber
[Digression]
Now that you know how to distribute one argument to two intermediate functions and the result to a binary function, there is the somehow related case that you want to distribute two arguments to one intermediate function and the results to a binary function:
import Data.Function
orFst = (||) `on` fst
-- orFst (True,3) (False, 7)
--> True
This pattern is e.g. often used for the compare function.