This extends from a solution I have used for my this question
data CoroMap a = CoroMap (ColorMap (RoleMap a))
instance Functor CoroMap where
fmap fn (CoroMap coro) =
CoroMap $ fmap (fmap fn) coro
visions:: Piese -> [Vision]
visions p = []
ihavethis :: CoroMap [Piese]
ineedthis:: CoroMap [Vision]
-- ineedthis = ???
-- I have this a -> [b]
-- I need this [a] -> [b]
-- specifically converting CoroMap [Piese] to CoroMap [Vision]
-- using Piese -> [Vision]
CodePudding user response:
I have this a -> [b]
I need this [a] -> [b]
A lot of questions like this in Haskell basically boil down to "following the types" - meaning that (sometimes, although not always) you don't have to think too hard about what you actually want the function to do, you just need the types to match up. (This might seem rather miraculous, and in a way it is - it's due to the fact that types in Haskell give you so much information compared to many other statically-typed languages.) And the ability to pick a function which has the right type often simply comes with a bit of experience.
Even if you don't have much experience, there is a very good way to "cheat" - go to Hoogle and type in the type signature you are interested in. Here, entering
(a -> [b]) -> [a] -> [b]
and letting it search will bring up a lot of relevant results, of which the very first is concatMap. This is the function that, given such a function of type a -> [b]
and a list of type [a]
, first maps the function over the list, resulting in a list of lists ([[b]]
), then concatenates those together to get a simple [b]
. That's very likely what you want (and seems to be the case, based on your response to my comment suggesting it).
In terms of being able to spot this without needing a helper like Hoogle - Monads are ubiquitous in Haskell, and lists are one of many very common type which form a monad. Seeing therefore that you have a a -> m b
(for a particular monad m
) and want a m a -> m b
, any Haskeller with a little bit of experience will instantly think of the fundamental method of the Monad
typeclass, which is (>>=)
(pronounced "bind"), and has type
(Monad m) => m a -> (a -> m b) -> m b
This is very close to what you want, the only difference is that you have to flip it, to get:
flip (>>=) :: (Monad m) => (a -> m b) -> m a -> m b
which due to currying (aka "arrows in type signatures are right-associative"), is the same as
flip (>>=) :: (Monad m) => (a -> m b) -> (m a -> m b)
which is exactly what you asked for, only it will work for all Monads, not just lists. That's how I came up with concatMap
, because (>>=)
for lists is defined to be flip concatMap
, so the flip (>>=)
that you want is simply concatMap
.
Note also that the Hoogle search I mentioned above lists (=<<)
a short way down - that's just an abbreviation for flip (>>=)
. So this all marries up nicely.
Finally, for your bonus question:
I need this function
f [a] -> (a -> [b]) -> f [b]
where from context f
is a Functor
- this is just a case of combining what we already have:
concatMap :: (a -> [b]) -> ([a] -> [b])`
with fmap
. Given g :: a -> [b]
we know concatMap g
is of type [a] -> [b]
, so fmap (concatMap g)
is of type f [a] -> f [b]
. In other words, \g -> fmap (concatMap g)
(which you can also write as fmap . concatMap
) has type (a -> [b]) -> f [a] -> f [b]
, so we simply need to apply swap the arguments. So the function you want is
flip (fmap . concatMap)
in pointfree form, or if you prefer explicit arguments (which looks less elegant but is often easier to understand), you can write it as
\as g -> fmap (concatMap g) as