Is there a known composition function for the type signature (a -> b) -> (b -> c) -> (a -> (b, c))?

I've been working for a while in an algorithm that requires composing it's function parts so that the return value of the composition contains the output of both functions. For example (in JavaScript):

const double = x => x * 2
const increment = x => x + 1

const doubleThenIncrement = composeConservingOutputs(increment, double)

doubleThenIncrement(2) -- => [4, 5]

I've been wondering if this is a known composition pattern. In Haskell, this seems to be achievable in a clean way piping the output of one arrow to the fanout of the other arrow and id:

import Control.Arrow 

double = (*) 2
increment = (+) 1

doubleThenIncrement = arr double >>> (arr id &&& arr increment)

but I couldn't find if this composition is a common/known pattern or not.

I find this useful because it allows to very easily inspect the output of the intermediate functions for debugging, which made me think this is probably a thing already.

A Hoogle search for the pattern (a -> b) -> (b -> c) -> (a -> (b, c)) proves unfruitful.

Thanks

Upvotes: 2

Views: 188

Answers (2)

Will Sewell
Will Sewell

Reputation: 2643

It's tricky to come up with general solution for what you want in Haskell because the intermediate values need to be of the same type.

The pattern of logging out intermediate values is something that the Writer monad is suited to. The restriction here is that the "written" values must implement the Monoid typeclass. I suspect this is OK in your case if you are just using this for debugging, i.e. you can convert the values to String, which is a Monoid.

I would define a function writeParam f x = (show x, f x) if you like arrows) which you can wrap all your functions in. Functions which return tuples Monoid a => ((,) a) are actually a simple implementation of the writer monad, so you can then compose these functions with regular monad composition like so:

doubleThenIncrement :: (Num n, Show n) => n -> (String, n)
doubleThenIncrement = writeParam double >=> writeParam increment

By the way, if you like arrows, you could also define writeParam as writeParam = (show &&&) or even just writeParam = (id &&&) if you want to be more generic. Although IMO this is less clear.

Upvotes: 2

chepner
chepner

Reputation: 530940

With no additional imports, you can use

doubleThenIncrement = fmap increment . (id >>= (,)) . double

This function first doubles its argument, then uses the Monad instance of functions to use return value of double as both arguments for the (,) function, then applies increment to the second of the elements in the resulting tuple.

With one import, you can simplify the middle part a little:

import Control.Monad
doubleThenIncrement = fmap increment . join (,) . double

(For functions, the implementation of join is effectively join f = \x -> f x x. We call (,) using the result of double for both arguments. join (,) . double is \x -> (,) (double x) (double x) is (double x, double x).)

You can define a function that takes increment and double as arguments to return doubleThenIncrement:

> let composePreservingOutputs f g = fmap f . join (,) . g
> composePreservingOutputs increment double $ 2
(4,5)

In all its horrific point-free glory, it would be

-- Courtesy of pointfree.io
composePreservingOutputs = (. (join (,) .)) . (.) . fmap

(Or, you could just write

composePreservingOutputs f g x = (f x, g (f x))

but where's the fun in that?)

Upvotes: 4

Related Questions