Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 months ago.
Improve this question
I am trying to create a Haskell function to generate a Cartesian product of two lists
Here's my attempt:
cartesianProduct :: (a -> b -> c) -> [a] -> [b] -> [c]
cartesianProduct f x [] = x
cartesianProduct f y [] = y
cartesianProduct = f x y unionHelper func1 f xs ys
If you're in a hurry, then the function is called liftA2 and it's actually built-in.
sort $ liftA2 (*) [1,2,4] [1,3,9] -- [1,2,3,4,6,9,12,18,36]
But, of course, that's not very enlightening, so let's talk about how we might do it ourselves.
The list type, [], is a functor. We can implement fmap directly using recursion.
fmap :: (a -> b) -> [a] -> [b]
fmap _ [] = []
fmap f (x:xs) = f x : fmap f xs
Now we have a way of to do something for each element of a list. Great, that's how we'll apply our final operation, but we need to generate all of the argument lists as well. Specifically, we want to produce the Cartesian product.
cross :: [a] -> [b] -> [(a, b)]
And we can do so recursively as well, using our fmap function to help out.
cross :: [a] -> [b] -> [(a, b)]
cross [] _ = []
cross (x:xs) ys = fmap ((,) x) ys ++ cross xs ys
Recursion on the first element. If the first argument is empty, then the Cartesian product is empty. If the first argument is nonempty, then the result should consist of the head paired with each (via fmap) element of the second argument, followed by the Cartesian product of the tail with the second list.
Now we have a way to get a [(Int, Int)]. And we want to multiply those elements, so it's just a matter of tying it all together.
apply2 :: (a -> b -> c) -> [a] -> [b] -> [c]
apply2 f xs ys = fmap (\(x, y) -> f x y) $ cross xs ys
Take the Cartesian product of the two lists, then apply our function f (uncurried) to each element.
This gives us our result, in a slightly different order than in your example. But it looks like you're sorting the results anyway, rather than producing them in an algorithmic order.
sort $ apply2 (*) [1,2,4] [1,3,9] -- [1,2,3,4,6,9,12,18,36]
Now, let's talk about why liftA2 works. The list type is an example of an applicative functor, represented by the Applicative typeclass. Whereas fmap (which is part of Functor) takes a single function and maps it over a functor (in our case, over several elements of a list), the fundamental applicative operation is <*> (pronounced "ap"), whose signature is
fmap :: Functor f => (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Instead of mapping a single simple function over a functor value f a, we're mapping a function inside the functor f (a -> b) over a value also inside the functor f a. Now, this is a jumble of words, but in the case of lists it looks like
fmap :: (a -> b) -> [a] -> [b]
(<*>) :: [a -> b] -> [a] -> [b]
Rather than applying a single function to several elements, we're applying several functions to several elements. (<*>) is actually just our cross function written in a slightly different way. It's combining two lists using every combination to produce a result of some type. (In fact, the relationship between our cross and the applicative (<*>) is rather deep mathematically, due to the fact that (->) and (,) are adjoint to each other, but that's getting off track)
So (<*>) is just cross by another name. And liftA2 is defined as (something equivalent to)
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = fmap f a <*> b
It's just doing fmap and (<*>), which is basically what our apply2 function was doing to begin with. Again, it's slightly shuffled around since we're using (->) rather than (,) (since our "ap" operator applies a function rather than building a tuple), but it's exactly the same idea.
Certainly, the liftA2 approach is snazzy and short, but don't let it blind you. There's nothing wrong with a recursive approach, especially as you're learning. If apply2 makes more sense to you and my explanation above of the applicative functors was nonsense, then go for it. You can go a long way in Haskell learning about recursion and data structures before really grokking functors, and that's fine. But when you're ready, I recommend Typeclassopedia for an excellent summary of the Haskell typeclasses that represent functors, applicative functors, monads, and all of the other lesser-known abstractions available to the programmer.
Good luck, and happy coding!
Related
Monad can pass Just [1,2], which is a different type from what the original length function takes, to >>= return . length.
Just [1,2] >>= return . length
Can I say that Monad makes it possible to see Maybe [a] as isomorphic with [a] on length using (>>=, return)? (Of course they are not really isomorphic.)
Can I choose the term "isomorphic" this situation?
What your example ultimately illustrates is that Maybe is a functor: if you have some f :: a -> b, you can use fmap to turn it into fmap f :: Maybe a -> Maybe b in a way that preserves identities and composition. Monads are functors, with \f m -> m >>= return . f being the same as fmap f m. In your case, we have the length function being transformed by the Maybe functor.
can I choose term "isomorphic" this situation?
Not really. fmap for Maybe is not an isomorphism. An isomorphism requires there being a two-sided inverse that undoes it, which in this case would be something like:
unFmapMaybe :: (Maybe a -> Maybe b) -> (a -> b)
-- For it to be a two-sided inverse to `fmap`, we should have:
unFmapMaybe . fmap = id
fmap . unFmapMaybe = id
However, there are no (Maybe a -> Maybe b) -> (a -> b) functions, as there is no way to obtain a b result if the input Maybe a -> Maybe b function gives out a Nothing. While there are specific functors whose fmap is an isomorphism (Identity is one example), that is not the case in general.
[a] is isomorphic to the quotient type of Maybe [a] with Nothing and Just [] considered equivalent. Alternatively it is isomorphic to Maybe (NonEmpty a), which simply eliminates the Just [] case.
In other words, [a] can be factorized as a composition of the Maybe and NonEmpty functors.
A result of this is that you can lift any function on NonEmpty a to a function on [a]:
liftEmptyable :: (NonEmpty a -> r) -> [a] -> Maybe r
liftEmptyable _ [] = Nothing
liftEmptyable f (x:xs) = Just $ f (x:|xs)
Not sure that actually has much to do with your question though. As duplode answered, you don't really do anything but a simple functor mapping. We could at most elaborate that the monad laws ensure that the fmap really behaves as if length acted directly on the contained list:
Just [1,2] >>= return . length
≡ return [1,2] >>= return . length -- def. of `Monad Maybe`
≡ return (length [1,2]) -- left-identity monad law
I'm reading the second edition of Programming in Haskell and I've came across this sentence:
... there is only one way to make any given parameterised type into a functor, and hence any function with the same polymorphic type as fmap must be equal to fmap.
This doesn't seem right to me, though. I can see that there is only one valid definition of fmap for each Functor type, but surely I could define any number of functions with the type (a -> b) -> f a -> f b which aren't equivalent to each other?
Why is this the case? Or, is it just a mistake by the author?
You've misread what the author was saying.
...any function with the same polymorphic type as fmap...
This means, any function with the signature
Functor f => (a -> b) -> f a -> f b
must be equivalant to fmap. (Unless you permit bottom values, of course.)
That statement is true; it can be seen quite easily if you try to define such a function: because you know nothing about f except that it's a functor, the only way to obtain a non-⊥ f b value is by fmapping over the f a one.
What's a bit less clear cut is the logical implication in the quote:
there is only one way to make any given parameterised type into a functor, and hence any function with the same polymorphic type as fmap must be equal to fmap.
I think what the author means there is, because a Functor f => (a -> b) -> f a -> f b function must necessarily invoke fmap, and because fmap is always the only valid functor-mapping for a parameterised type, any Functor f => (a -> b) -> f a -> f b will indeed also in practice obey the functor laws, i.e. it will be the fmap.
I agree that the “hence” is a bit badly phrased, but in principle the quote is correct.
I think that the quote refers to this scenario. Assume we define a parameterized type:
data F a = .... -- whatever
for which we can write not only one, but two fmap implementations
fmap1 :: (a -> b) -> F a -> F b
fmap2 :: (a -> b) -> F a -> F b
satisfying the functor laws
fmap1 id = id
fmap1 (f . g) = fmap1 f . fmap1 g
fmap2 id = id
fmap2 (f . g) = fmap2 f . fmap2 g
Under these assumptions, we have that fmap1 = fmap2.
This is a theoretical consequence of the "free theorem" associated to fmap's polymorphic type (see the comment under Lemma 1).
Pragmatically, this ensures that the instance we obtain from deriving Functor is the only possible one.
It is a mistake. Here's some examples of functions with the same type as fmap for lists that are not fmap:
\f -> const []
\f -> concatMap (replicate 2 . f)
\f -> map (f . head) . chunksOf 2
\f -> map f . reverse
There are many more. In general, given a function ixf from list lengths to lists of numbers no bigger than that length (that is, valid indices into the list), we can build
maybeIt'sFmapLol :: (Int -> [Int]) -> (a -> b) -> [a] -> [b]
maybeIt'sFmapLol ixf elemf xs = [map elemf xs !! ix | ix <- ixf (length xs)]
Use suitably lazy variants of Int to handle infinite lists. A similar function schema can be cooked up for other container-like functors.
Is it possible in Haskell do define a type similar to ziplist, in which operation a <*> b will produce list which is as long as the longest of a and b.
It is clear that in this case we must assume that a and b are lists over something like Monoid, so tentative declaration is:
instance Monoid a => Applicative (ZList a) where ...
which clearly will not typecheck. Another tentative approach is to use GADTs with constrained constructors, something like
data ZList a where
Z:: ZList a
S:: Monoid a => a-> (ZList a) -> (ZList a)
but then I stuck on a stage of making it Functor because we cannot guarantee that in fmap::(a -> b) -> f a -> f b, b will be Monoid.
Clearly, this question extends to wider class of algebraic datatypes for which we want to define "pointwise" applicative behavior in which we produce output with shape similar to the union of shapes of the arguments.
First, what you really want is probably Default, not Monoid - you have no use for mappend.
I don't think anything useful is possible in Applicative itself. That said, I can define a version of (<*>) (called (<#>)) with extra constraints that lets me do what I think you have in mind.
Why there is no point in making a new data type
First, suppose we were to take the ExistentialQuantification route in hopes of pushing our constraints into the data and having legitimate instances of Functor and Applicative. That blows up as soon as we try to define fmap:
{-# LANGUAGE ExistentialQuantification #-}
data ZipMonList a = Default a => ZipMonList [a]
-- Woops, we still need a constraint for `Default b`
fmap :: Default b => (a -> b) -> ZipMonList a -> ZipMonList b
fmap f (ZipMonList xs) = ZipMonList (f xs)
So, with that settled, let's stick to the ZipList type (since we want the same (<$>) anyways) and just define our new constrained version of (<*>), called (<#>).
Make (<#>) for ZipList
Underlying ZipLists (<*>) is the zipWith function. We need something similar for (<#>), but that extends lists. Then, (<#>) looks a lot like (<*>):
import Control.Applicative (ZipList(..))
import Data.Default
-- Like 'zipWith', but has maximum length
zipWith' :: (Default a, Default b) => (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' f [] [] = []
zipWith' f (x:xs) [] = f x def : zipWith' f xs []
zipWith' f [] (y:ys) = f def y : zipWith' f [] ys
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
-- same fixity as <*>
infixl 4 <#>
-- like '(<*>)', but uses 'zipWith'' instead of 'zipWith'
(<#>) :: (Default a, Default b) => ZipList (a -> b) -> ZipList a -> ZipList b
ZipList fs <#> ZipList xs = ZipList (zipWith' id fs xs)
And I can do a test run on tuples:
ghci> (,,) <$> ZipList [1.2,3.4,5.6,7.8,9.1] <#> ZipList [[(),()],[()],[(),(),()]] <#> ZipList [1,2,3,4]
ZipList {getZipList = [(1.2,[(),()],1),(3.4,[()],2),(5.6,[(),(),()],3),(7.8,[],4),(9.1,[],0)]}
Key takeaway point: this is not an Applicative, but still doable.
I just have a few notes for you, things to think about.
The definition of the typeclass that will allow this is known as the Constrained Typeclass Problem, and there have been a few approaches.
I notice that you have only specified that the resulting list should be as long as the longer of the two lists, but you haven't said what the remaining elements should be. At that point you might as well use the applicative
ZipList :*: Const (MaxPos Int)
(where :*: is functor product, and MaxPos is a monoid I just made up taking the maximum on nonnegative numbers) which keeps track of the "length" separately, because the remaining elements will be meaningless.
Rather, I suspect you mean something where the remaining elements are preserved in some sense, i.e. so
(*) <$> [2,3,4] <*> [4] = [8,3,4]
and also
(+) <$> [2,3,4] <*> [4] = [6,3,4]
So if we were to "fill in" missing elements in the former case we should fill them in with 1, and in the latter we should fill them in with 0. This starts to show us a different aspect of the problem; we need to pick identity elements based on the operation, or just "leave alone" the remaining elements (which constrains the operations to type a -> a -> a). This is looking less possible, it'd be interesting to explore more. That's all I've got for now, sorry.
I'm working on an exercise from Brent Yorgey's 2013 UPenn lecture to implement mapA.
mapA :: Applicative f => (a -> f b) -> ([a] -> f [b])
I'm trying to gain intuition for this function. How is this function useful? I'm not questioning its utility - just trying to understand it.
Additionally, I'm looking for a hint to go from a -> f b to [a] -> f [b].
If we knew nothing at all about f then a function like a -> f b would let us put things into f-boxes but then we'd be completely stuck. You're probably familiar with Functor. If we know f were a Functor then we'd be able to transform the thing inside of f, but we're still basically stuck---f forms an unmoving wall we cannot cross.
Why do we care? Well, when we try to construct the function [a] -> f [b] we need to some how operate on a collection of as. We could, perhaps, just pull the first one off if we liked (and it existed) and feed it through a -> f b then wrap the result in a list:
unsatisfying :: Functor f => (a -> f b) -> ([a] -> f [b])
unsatisfying inject (a : _) = fmap (\x -> [x]) (inject a)
but not only do we have an incomplete pattern match on [a], we're clearly violating something in the spirit of this function---we'd much prefer to use all of the as. Unfortunately, knowing only f or even that f is a Functor gets us only as far as
stillUnsatisfying :: Functor f => (a -> f b) -> ([a] -> [f b])
stillUnsatisfying inject as = map inject as
The problem is that just because we have a collection of f-containers doesn't mean we can find any way to treat them collectively. We'd like to somehow "glue" our collection [f b] all together. If we could do that then a function like [a] -> f [b] would sound like "explode our list [a] into pieces, pass them each individually into f using inject, glom all of the (f b)s together, and then reassemble the list on the inside".
Clearly we need a way to "glom" Functors together and also a way to operate on the separate pieces "on the inside" of f.
So this is where Applicative comes in. I'm not going to introduce it exactly, though. Instead, let's look at an equivalent type class
class Functor f => Monoidal f where
basic :: a -> f a
glom :: f a -> f b -> f (a, b)
It's an interesting exercise to prove that Monoidal and Applicative are equivalent, but immediately you can see that glom provides exactly what we're looking for. Moreover, basic/pure give us the ability to inject raw pieces of our list into f as we need them (for instance, if our [a] is empty then we'll need to inject an empty list into f without using a -> f b as we cannot---that looks like basic [] :: f [b]).
So Applicative provides you the ability to not only transform inside of functors but also to glom a bunch of functors together and operate on all of their pieces inside of the functor.
You're halfway there, but the final function you're looking for is the sequenceA function that's defined in LYAH. Here's my implementation:
sequenceA :: Applicative f => [f a] -> f [a]
sequenceA = foldr ((<*>) . fmap (:)) (pure [])
From there the mapA function is easy as pie. Just add another argument and compose the two functions:
mapA :: Applicative f => (a -> f b) -> [a] -> f [b]
mapA f xs = foldr ((<*>) . fmap (:) . f) (pure []) xs
-- Or, you could implement it this more elegant, albeit slightly slower, way:
mapA = (sequenceA .) . map
And there you have an implementation for you to have a look at. As for the usefulness, it's particularly useful in IO, when you have a list like ["Foo","Bar","Baz"], and you'd like to putStrLn all of those functions. Do do so, you will need to map each value and the sequence it, which mapA does. Note that these functions have monadic equivalents, but it's a good exercise to implement them in purely Applicative ways.
These functions are both quite useful when dealing with lists of Applicatives, allowing one to more easily manipulate the values inside them without using half a ton of fmaps.
We already know that map :: (a -> b) -> ([a] -> [b]) is useful. It applies a function to every element of a list.
A loose but enlightening interpretation of a -> f b for an applicative f is that it is a function which takes an a, performs an applicative "action", and returns a b. For example if f is IO then the "action" might be reading from a disk. mapA :: (a -> f b) -> ([a] -> f [b]) can be interpreted as applying this "function" to every element of the list.
When reading stuff on Haskell, I sometimes come across the adjective "applicative", but I have not been able to find a sufficiently clear definition of this adjective (as opposed to, say, Haskell's Applicative class). I would like to learn to recognize a piece of code/algorithm/data structure, etc. that is "applicative", just like I can recognize one that is "recursive". Some contrasting examples of "applicative" vs. whatever the term intends to draw a distinction from (which I hope is something more meaningful in its own right than "non-applicative") would be much appreciated.
Edit: for example, why was the word "applicative" chosen to name the class, and not some other name? What is it about this class that makes the name Applicative such a good fit for it (even at the price of its obscurity)?
Thanks!
It's not clear what "applicative" is being used to mean without knowing the context.
If it's truly not referring to applicative functors (i.e. Applicative), then it's probably referring to the form of application itself: f a b c is an applicative form, and this is where applicative functors get their name from: f <$> a <*> b <*> c is analogous. (Indeed, idiom brackets take this connection further, by letting you write it as (| f a b c |).)
Similarly, "applicative languages" can be contrasted with languages that are not primarily based on the application of function to argument (usually in prefix form); concatenative ("stack based") languages aren't applicative, for instance.
To answer the question of why applicative functors are called what they are in depth, I recommend reading
Applicative programming with effects; the basic idea is that a lot of situations call for something like "enhanced application": applying pure functions within some effectful context. Compare these definitions of map and mapM:
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
mapM _ [] = return []
mapM f (x:xs) = do
x' <- f x
xs' <- mapM f xs
return (x' : xs')
with mapA (usually called traverse):
mapA :: (Applicative f) => (a -> f b) -> [a] -> f [b]
mapA _ [] = pure []
mapA f (x:xs) = (:) <$> f x <*> mapA f xs
As you can see, mapA is much more concise, and more obviously related to map (even more so if you use the prefix form of (:) in map too). Indeed, using the applicative functor notation even when you have a full Monad is common in Haskell, since it's often much more clear.
Looking at the definition helps, too:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Compare the type of (<*>) to the type of application: ($) :: (a -> b) -> a -> b. What Applicative offers is a generalised "lifted" form of application, and code using it is written in an applicative style.
More formally, as mentioned in the paper and pointed out by ertes, Applicative is a generalisation of the SK combinators; pure is a generalisation of K :: a -> (r -> a) (aka const), and (<*>) is a generalisation of S :: (r -> a -> b) -> (r -> a) -> (r -> b). The r -> a part is simply generalised to f a; the original types are obtained with the Applicative instance for ((->) r).
As a practical matter, pure also allows you to write applicative expressions in a more uniform manner: pure f <*> effectful <*> pure x <*> effectful as opposed to (\a b -> f a x b) <$> effectful <*> effectful.
On a more fundamental level one could say that "applicative" means working in some form of the SK calculus. This is also what the Applicative class is about. It gives you the combinators pure (a generalization of K) and <*> (a generalization of S).
Your code is applicative when it is expressed in such a style. For example the code
liftA2 (+) sin cos
is an applicative expression of
\x -> sin x + cos x
Of course in Haskell the Applicative class is the main construct for programming in an applicative style, but even in a monadic or arrowic context you can write applicatively:
return (+) `ap` sin `ap` cos
arr (uncurry (+)) . (sin &&& cos)
Whether the last piece of code is applicative is controversial though, because one might argue that applicative style needs currying to make sense.