0
internalAnd :: Bool -> Bool -> Bool
internalAnd True True = True
internalAnd _ _ = False

(&&) :: Applicative m => m Bool -> m Bool -> m Bool
(&&) = liftA2 internalAnd


-- Usage
greaterThan x = (x <)
lessThan x = (x >)

validateAge = greaterThan 0 && lessThan 120  -- It's really useful with combinators.

I think it is useful to define all functions over Applicative for many situation like making combinators. And applicative is abstraction of applicability that is corresponding to function's ability, so it seems not bad to do like this.

What is expected problems of defining all the functions over Applicative?

lunuy lunuy
  • 429
  • 3
  • 7
  • What do you mean by "defining all the functions over Applicative"? It's not clear what you're asking. Clearly it is useful, given any `f :: a -> b -> c` to use `liftA2 f :: (Applicative m) => m a -> m b -> m c`, as you demonstrate - but that's easy to do (as, once again, you demonstrate). Are you expecting library authors to give a separate name to `liftA2` applied to all functions of 2 arguments? – Robin Zigmond Mar 27 '22 at 12:32
  • 5
    In my opinion, this seems to be almost missing the point of `Applicative`. One reason `Applicative` is useful is that you *don't* need to do this: It provides a flexible and succinct way to take any function and turn it into a function over an `Applicative` (using `liftA2` or `<*>` with `<$>`). Also, as a direct result of this, you can easily avoid the downsides of defining every function over `Applicative` mentioned in @Noughtmare's answer by just not doing it (since you don't need to!) – David Young Mar 27 '22 at 19:04

1 Answers1

8

One disadvantage is that you now cannot use it for normal booleans anymore. So, you'd have to write pure True && pure False instead of True && False. And if you want to get at the result you'd have to use runIdentity :: Identity a -> a or something like that.

A more subtle problem is that your proposed function is less lazy. Usually, programmers expect && to be short-circuiting. So it should still return False if you write False && undefined, but your function will get stuck if you write pure False && undefined. Really you'd want to implement it as a monad:

(&&) :: Monad m => m Bool -> m Bool -> m Bool
x && y = do
  x' <- x
  if x
    then y
    else pure False

A common alternative is to give it another name, e.g. <&&>.

Noughtmare
  • 9,410
  • 1
  • 12
  • 38
  • 3
    Also, the [original paper on applicatives](https://www.staff.city.ac.uk/~ross/papers/Applicative.pdf) talked about a notation called *idiom brackets* which was used to automatically lift any function. Instead of writing `liftA2 (&&) x y`, idiom brackets would let you write `⟦ x && y ⟧` (or `i[ x && y ]i` in plain ASCII: "i" for "idiom"). – chepner Mar 27 '22 at 21:48
  • (Which, to be clear, is not to say that you can *do* this in Haskell; they mentioned a possibility of implementing such notation using multitype parameter classes, but I'm not aware of any such implementation being made.) – chepner Mar 27 '22 at 22:05