Tacit function composition in Haskell
Asked Answered
D

2

13

Say I have a mean function defined like so:

mean xs = sum xs / (fromIntegral $ length xs)

but I want it in some tacit form, like this:

mean = sum / (fromIntegral . length)

Is there a built-in Haskell way to do something along these lines without having to build up my own tacit function (something like this):

tacit :: (a -> b -> c) -> (d -> a) -> (d -> b) -> d -> c
tacit a b c i = a (b i) (c i)

In this form, the function looks like this:

mean = tacit (/) sum (fromIntegral . length)

but it feels like there might be a way to avoid having to use an explicit function such as this. I'm just wondering; is there some way to do this that is built in to Haskell?

Deepfreeze answered 21/8, 2012 at 18:49 Comment(3)
See squing.blogspot.com/2008/11/beautiful-folding.html, conal.net/blog/posts/more-beautiful-fold-zippingRatchford
The technical term for what you call "tacit" is "point free" (or sometimes "pointless" for people being sarcastic). The term "point" comes from topology, and it actually refers to the variables.Accrete
@PaulJohnson, "tacit" is the term used by the J programming language (and maybe others?) to refer to what is more commonly known as "point free".Admit
A
20

Applicative functors work pretty well here.

import Control.Applicative

mean = (/) <$> sum <*> (fromIntegral . length)
Aerophagia answered 21/8, 2012 at 20:10 Comment(0)
B
14

Yes, your tacit function is liftM2 in the (->) r monad (liftM2 is in Control.Monad, and the function instance of Monad is in Control.Monad.Instances).

I found this using the pointfree program (you can install it via cabal install pointfree), invoked as:

$ pointfree '\xs -> sum xs / (fromIntegral $ length xs)'

(in a Unix terminal)

Blus answered 21/8, 2012 at 18:57 Comment(8)
And then you can make a Num instance for (->) r and then mean = sum / (fromIntegral . length) will actually work.Rishi
@SjoerdVisscher: you need Fractional too because of (/). Just tried it and confirmed that's all you need.Pori
And an amusing addition to that is that once you have the Num r => Num (a -> r) instance, you can add addition to itself: (+) + (+) becomes a well-typed expression (equivalent to \x y -> (x + y) + (x + y)).Pori
@sacundim Ah, right. And 1 2 3 4 is also a well-typed expression. It's clear why this is not a built-in instance.Rishi
Heh, I didn't think of that one. But what makes it worse is that if a module of yours imports any module that uses this instance directly or indirectly, you're going to get this instance. So one should really wrap it with a newtype—in which case the extra syntactic overhead means that you might as well use Applicative.Pori
@Sjored, OK, I give in, what does 1 2 3 4 give you?Accrete
@PaulJohnson, 1, assuming fromIntegral = const . fromIntegral.Blus
As the maintainer of pointfree, I ought to point out (no pun intended) that most of the testsuite currently fails (has been doing so since before I inherited it, I might add!)Disrespectable

© 2022 - 2024 — McMap. All rights reserved.