1

What should I use if I want to have something like

[a->b] -> a -> [b]

basically I have a list of functions, all take in a value a and returns b. I want to apply all of them to one a and get the results [b].

Which one should I use?

Thanks

Will Ness
  • 70,110
  • 9
  • 98
  • 181

3 Answers3

10

You don't need Traversable, just Functor:

swingMap f x = fmap ($ x) f

See also the swing function (this is equivalent to swing fmap).


Or, if you're using Edward Kmett's distributive library, you can have the best of both this answer (only a Functor rather than a Traversable) and chi's answer (avoiding defining a custom function for the job), by using distribute from Data.Distributive.

6

You could use sequence, specialized to the (->) a monad. In this way you can avoid defining a custom function for the job -- it's already there.

> :t sequence
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
> :t sequence :: [a->b] -> a -> [b]
sequence :: [a->b] -> a -> [b] :: [a -> b] -> a -> [b]
> sequence [id,(10+),(10-)] 3
[3,13,7]

(sequenceA and traverse id, being the same, would also work)

chi
  • 111,837
  • 3
  • 133
  • 218
  • This definitely looks like overkill, as the other answer shows. – dfeuer Mar 10 '20 at 01:13
  • Although having thought about this a bit more, I found a solution that combines the benefits of both ways (edited into my answer now). – Joseph Sible-Reinstate Monica Mar 10 '20 at 01:40
  • 1
    @dfeuer I don't know what's overkill in this context. The OP only asked for a specific function on lists of functions. Both my answer and Joseph's (after the edit) use a general library function to do the job. Perhaps it's overkill because it's general? Is it better to use a custom function? I am unsure. Note that nowadays `sequence` is in scope without importing anything, so it's not completely unreasonable to ask that the programmer understands it. – chi Mar 10 '20 at 08:33
  • 1
    @chi I think dfeuer's point was that the `Traversable` constraint is overkill. – Joseph Sible-Reinstate Monica Mar 10 '20 at 11:01
  • @JosephSible-ReinstateMonica It's a matter of perspective. If you need it on lists, and lists only, like the OP asked, `Traversable []` is satisfied, so no constraint remains to be required from the user. If you want to generalize it to other "containers", and you need a constraint which can not be solved immediately, then I agree with you. – chi Mar 10 '20 at 11:22
  • I mostly meant that the *idea* of `sequence` is overkill when the significantly simpler idea of `fmap` will do. And using reader here seems excessive. The constraint is also stronger than needed, which you've justified but I find a bit sad. – dfeuer Mar 10 '20 at 15:52
  • 2
    @dfeuer `fmap` is significantly simpler, but that alone does not solve the problem; we need `\f x -> fmap ($ x) f` which is not as simple, IMO. I don't think there's a completely objective notion of "simpler" here. To me, we need to turn `[a->b]` into `a->[b]`, i.e. commuting `[]` and `a ->`, which `sequence`/`distribute` do nicely and simply. Others might disagree, of course :-) – chi Mar 10 '20 at 17:01
1

When functions appear in a data type it's time to remember applicatives because that's what it is. One might easily do this job like;

appList :: [a->b] -> a -> [b]
appList fs x = fs <*> pure x

λ> appList [(+1), (*2), subtract 3] 5
[6,10,2]
Redu
  • 25,060
  • 6
  • 56
  • 76
  • The interchange law can (and IMO should) be applied there. – Joseph Sible-Reinstate Monica Mar 10 '20 at 11:02
  • @Joseph Sible-Reinstate Monica I had to look up for [The Interchange Law on Applicatives](https://stackoverflow.com/q/27285918/4543207) but cant really tell what's the catch. – Redu Mar 10 '20 at 11:49
  • @JosephSible-ReinstateMonica, the existence of the special cases `fs <*> pure x` and `pure f <*> xs` inspired me to rewrite the `Applicative` instance for `Data.Sequence`. Interestingly, the `fs <*> pure x` case proved to be a *far* more productive starting point in that process. – dfeuer Mar 10 '20 at 16:00