1

I am learning Haskell. One of the exercises I was asked to do was to compute all sums of a power set of a set of integers, e.g.:

allSums [1, 2, 5] -- should be [8,3,6,1,7,2,5,0]

I came up with this after reading about applicatives and functors, to which lists belong. It worked.

allSums :: [Int] -> [Int]
allSums [] = [0]
allSums (x:xs) =
  if x == 0 
  then allSums xs
  else (+) <$> [x, 0] <*> allSums xs

I was shook. It looks so terse, but it appears correct.

I do not know whom to talk to. My friends and parents think I am crazy. How do you pronounce <$> and <*>? How do you even describe to people what they do?

duplode
  • 33,731
  • 7
  • 79
  • 150
daikonradish
  • 682
  • 5
  • 14
  • Another approach you could try would be `allSums = map sum . filterM (const [False, True])`. It doesn't have quite the same semantics, because you do some weird special-casing of zero. To be truly faithful, you need `allSums = map sum . filterM (const [False, True]) . filter (/= 0)`. – amalloy Nov 17 '21 at 17:26
  • I voted to reopen because this asks for the _meaning_ of it, despite the "pronunciation" wording. – Will Ness Nov 17 '21 at 21:22
  • 1
    Yeah, I almost closed as a duplicate of the same question but I don't think this is really primarily about pronunciation. – amalloy Nov 17 '21 at 22:42
  • Want to really blow your mind? This can also be written as `allSums = map sum . traverse (\x -> nub [x,0])`. Besides being even more terse, it works for non-list containers. – Daniel Wagner Nov 17 '21 at 22:53
  • are you sure you commented under the answer you intended to? – Will Ness Nov 18 '21 at 18:20

2 Answers2

3

You pronounce this combination pattern of <$> and <*> as liftA2:

liftA2 :: Applicative f 
       => (a -> b -> c) -> f a -> f b -> f c

So then in your case f ~ [], (+) :: Num a => a -> a -> a, and

liftA2 :: Num a 
       => (a->a->a) -> [a] -> [a] -> [a]
--        (+)  ...

(+) <$> [x,0] <*> allSums xs
=
[(x +), (0 +)] <*> allSums xs
=
liftA2 (+)  [x,0]  (allSums xs)
=
[x + r | x <- [x,0], r <- allSums xs]

and what they "do" in the case of lists is to form the Cartesian product-like combinations of the constituents and apply the function to each of the combinations.

In your case of summing the elements, using 0 for an element is like skipping over it completely. Thus indeed implementing the sums of the power set of numbers:

allSums [x1, x2, ..., xn]
=
[x1 + r | x1 <- [x1,0], r <- allSums [x2, ..., xn]]
=
[x1 + r | x1 <- [x1,0], r <- [x2 + r | x2 <- [x2,0], r <- allSums [x3, ..., xn]]]
=
[x1 + x2 + r | x1 <- [x1,0], x2 <- [x2,0], r <- allSums [x3, ..., xn]]
=
...
=
[x1 + x2 + ... + xn + r | x1 <- [x1,0], x2 <- [x2,0], ..., xn <- [xn,0], r <- [0]]
=
[x1 + x2 + ... + xn | x1 <- [x1,0], x2 <- [x2,0], ..., xn <- [xn,0]]

As to the pronunciation, I read (+) <$> [x, 0] <*> allSums xs as "plus over x-and-0 over all sums of xes".

As Daniel Wagner comments under the question, [x, 0] could be tweaked into [x | x /= 0] ++ [0], for efficiency.

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

When reading these to myself, I do not pronounce them; they are just a visual glyph in my mind. If I must speak them aloud, I would say "eff-map" for <$> (possibly "eff-mapped onto" if there's some ambiguity) and "app" for <*> (possibly "applied to" if there's ambiguity), which come from the pre-Applicative names fmap and ap.

Also, I hate saying fmap out loud. For this reason and others, I would likely mentally transform this to

pure (+) <*> [x,0] <*> allSums xs

before I said it so that I only need to pronounce pure and (<*>). As an aside, I actually find it a little bit surprising that this style is not more common; especially when spreading applicative arguments across multiple lines, this is more uniform, as

pure f
    <*> a
    <*> b
    <*> c

doesn't need to treat the a line specially.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • i like this because it answers my original question, which not simply how to pronounce `<$>` and `<*>`, but why. – daikonradish Nov 18 '21 at 09:18
  • OMG I JUST DISCOVERED THAT LIFTA2 CAN BE USED IN CONJUNCTION WITH DATA CONSTRUCTORS. okay I mean, I should've expected that that would work, but still, what a delight. – daikonradish Nov 18 '21 at 09:52