Applicative Logic

Just wanted to share my little applicative logic library [Hackage, GitHub].

I wrote a blogpost a while ago which explains the ideas behind it using a few different examples. The introduction focuses on how this generalises the usual logical functions (and,or,all,any), but if it seems dry, just skip to the examples. I keep finding places in my Haskell code where these functions allow nice formulations of previously tedious parts. Especially the new function “convert” is way more useful than it has any right to be.

Let me know what you think!

11 Likes

A lot of these functions already exist under other names. Specifically:

  • Your any is asumMap from Relude
  • Your or is asum
  • Your all is foldMapA from Relude

See also the Ap and Alt newtypes from Data.Monoid. Ap embodies your generalized “and”, and Alt embodies your generalized “or”.

Also, IMO, you’re going a little bit overboard with pointfree. all = ($ true) . foldr . (liftA2 (<>) <$>) is really hard to understand, especially since you used <$> where . would do.

3 Likes

Thank you for the feedback, jospehcsible. Indeed, some of the functions already exist – I mentioned as much in the blogpost! I appreciate that you list them here for reference. What I wanted to contribute with the library was a consistent naming and way of thinking more systematically about this.

For instance: asumMap does not really say much about the semantics of the function, especially when situatied inside an expression. The function any is more clear:

readTChan `any` [chan0,chan1,chan2]

This clearly reads as reading from any of the listed channels. And the following is also quite satisfying:

(print `all`) :: (Foldable t, Show a) => t a -> IO ()

foldMapA print does not have quite the same verve!

(As to the pointfreeness, I have no regrets, except the <$> was supposed to be replaced with . (as I did for any), but I forgot about it – I will get to it in the next version).

1 Like

There’s one other simplification I’d recommend if you want to stay fully pointfree: using flip f x instead of ($ x) . f. For example, instead of any = ($ false) . foldr . ((<|>) .), doing any = flip (foldr . ((<|>) .)) false, and ditto for all.

1 Like

I considered that way of doing it, and it has its merits, but I think ($ x) . reads fine as “then finally apply the resulting function to x”. I would use flip if I thought that flipping was an essential part of what is going on. But I feel that, to me at least, the flip is not part of what is semantically going on here.

And just to point out that I am not deliberately obtuse here, I did not write:

any = (`id` false) . foldr . ((<|>) .)

Because, again I feel there is function application going on here, and not really any identity function involved.

1 Like