So in Haskell terminology, you want this:
passAround :: Monad m => (a -> m b) -> a -> m a
passAround f x = do
f x
return x
Read the type signature as “passAround
takes a function f :: a -> m b
, whose result is a monadic action (i.e., something that may have side-effects which can be sequenced in a well-defined order, thus the Monad m
constraint) with arbitrary result-type b
, and a value a
to pass this function. It yields a monadic action with result-type a
.”
To see what “functional programming construct” this might correspond to, let's first unroll this syntax. In Haskell, do
sequencing notation is just syntactic sugar for monadic combinators, namely,
do
foo
bar
is sugar for foo >> bar
. (This is a bit trivial really, the whole thing really only gets interesting when you also bind local results to variables.)
So,
passAround f x = f x >> return x
>>
itself is shorthand for the general monadic-chaining operator, namely
passAround f x = f x >>= const (return x)
or
passAround f x = f x >>= \y -> return x
(That backslash denotes a lambda function, in JavaScript it would read f(x) >>= (y)=>return x
.)
Now, what you really want all this for is, chaining multiple actions. In Javascript you would write g(passAround(f, x))
, in Haskell this is not just a function argument because it's still a monadic action, so you want another monadic chaining operator: g =<< passAround f x
or
passAround f x >>= g
If we expand passAround
here, we get
(f x >>= \y -> return x) >>= g
Now, here we can apply the monad laws, namely the associativity law, giving us
f x >>= (\y -> return x >>= g)
and now the left unit law
f x >>= (\y -> g x)
IOW, the whole composition collapses down to just f x >> g x
, which could also be written
do
f x
g x
...which is kind of, duh. What of it all? Well, the nice thing is that we can abstract over this monad-rewrapping, with a monad transformer. In Haskell, it's called ReaderT
. What you would do if you know that f
and g
both use the variable x
, you could exchange
f :: a -> m b
g :: a -> m c
with
f' :: ReaderT a m b
f' = ReaderT f
g' :: ReaderT a m c
g' = ReaderT g
The ReaderT
value constructor corresponds conceptually to your passAround
function.
Note that ReaderT a m c
has the form (ReaderT a m) c
or, ignoring the details, m' c
, where m'
is again a monad! And, using the do
syntax for that monad, you can simply write
runReaderT (do
f'
g'
) x
which would in JavaScript look, theoretically, like
runReaderT (() => {
f';
g';
}, x)
Unfortunately you can't actually write it this way because unlike Haskell, imperative languages always use the same monad for sequencing their operation (which roughly corresponds to Haskell's IO
monad). Incidentally, that's one of the standard description of what a monad is: it's an overloaded semicolon operator.
What you can certainly do however is implement a monad transformer on dynamic types in the functional part of the JavaScript language. I'm just not sure if it's worth the effort.
f => x => (f(x), x)
. – ExcellencySK
in the SKI combinator calculus. – Dani