5

Consider functions of type a -> b -> c, and the applicative values a1, a2 :: (Applicative f) => f a.

I wish to construct a function which may be applied to functions of type a -> b -> c to obtain values of type Applicative f :: f c. I can do this in the following way:

g :: (Applicative f) => (a -> b -> c) -> f c
g = \f -> f <$> a1 <*> a2

(The explicit lambda is deliberate, as I am considering the construction of this function at any level, not just the top level).

If I try to write g in point-free style:

g = (<$> a1 <*> a2)

I get the following compile error:

The operator `<$>' [infixl 4] of a section
    must have lower precedence than that of the operand,
      namely `<*>' [infixl 4]
    in the section: `<$> gen1 <*> gen2'

I could write this point-free implementation:

g = flip (flip liftA2 a1) a2

but I feel that this is less readable, and it is simpler to refactor the infix function-based implementation to, e.g., add another argument, than changing the above to use liftA3.

One can write a chain of compositions:

g = (<*> a2) . (<$> a1)

This achieves point-free style and it is simple to add arguments - but they get prepended on the left instead of appended on the right, so you lose the correspondence with the function type (a -> b -> c). Furthermore, with more arguments you end up with a much longer expression than just using a lambda as in the first implementation.

So, is there are nice, terse way to write the section I desire, or am I stuck with a lambda?

frasertweedale
  • 5,424
  • 3
  • 26
  • 38
  • in Scala you could have `_ <$> a <*> b` but Haskell has no such syntax. – Erik Kaplun Jun 10 '15 at 08:49
  • @ErikAllik That's interesting syntax! I wonder if someone's written a template-haskell extension for it. That'd be great. – AJF Jun 10 '15 at 11:50
  • but `_` has already been taken by typed holes; perhaps something similar though; although the lambda syntax is quite fine. – Erik Kaplun Jun 10 '15 at 12:00

2 Answers2

8

<*> operates on the result of <$>, so:

g = (<*> a2) . (<$> a1)
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • 2
    @frasertweedale: In that case, I’d just use your first method. It’s not as if it has to be a lambda, though. `g f = f <$> a1 <*> a2` – Ry- Jun 10 '15 at 06:18
  • 4
    @frasertweedale: Even when it’s not just the top level, you can use `let` etc.. Less verbosity doesn’t mean more clarity. – Ry- Jun 10 '15 at 07:18
6

I'm not really convinced pointfree can be made better than using an explicit argument here, but a couple more ideas:

  • You can use flip infix:

    g = liftA2 `flip` a1 `flip` a2
    
  • You can use >>> from Control.Category, which is . flipped:

    g = (<$> a1) >>> (<*> a2)
    
Ørjan Johansen
  • 18,119
  • 3
  • 43
  • 53