Search code examples
purescript

How to compose functions with two argumens?


I know I can do:

fn1 :: B -> C

fn2 :: (A -> B) -> (A -> C)
fn2 callback = callback >>> fn2

But how do I compose (>>>) a function with two arguments?:

fn3 :: (Z -> A -> B) -> (Z -> A -> C)
fn3 callback = callback (\z a -> fn1 z a # fn2) ✅

fn3' :: (Z -> A -> B) -> (Z -> A -> C)
fn3' callback = callback (???) fn2 -- >>> won't work 💥

What should ??? operator be to return be exactly like >>> but taking the additional parameter needed for fn1 before running fn2 as >>> won't work?

more concretely

I'm trying to FFI this function more elegantly

const functions = require("firebase-functions");

exports.onRequestImp = function (handler) {
  return functions.https.onRequest((request, response) => {
    handler(request)(response)();
  });
}
module Firebase.Functions.HTTP (onRequest) where

import Prelude

import Control.Promise (Promise, fromAff)
import Effect (Effect)
import Effect.Aff (Aff)
import Node.Express.Types (Request, Response)

foreign import onRequestImp :: (Request -> Response -> Effect (Promise (Unit))) -> Effect Unit

onRequest :: (Request -> Response -> Aff (Unit)) -> Effect Unit
onRequest handler = do
  onRequestImp
    ( \req res -> do
        handler req res # fromAff
    )

Solution

  • First of all, your type signatures seem way off. In your first code block, with the definition of fn3 that you provide, it should look like this:

    fn3 :: A -> C
    fn3 = fn1 >>> fn2
    

    Similarly, if I understood the spirit of the question right, in the second code block, the composed type should be:

    fn3 :: A -> B -> D
    

    That is, you take the output of fn1 and give it as input to fn2.


    With that out of the way, let's talk about composition. The answer is: yes and no.

    No, there is no such composition operator/function in the standard library, because it's usually not useful. Composing functions like this heavily obscures what's going on and makes your program that much harder to maintain.

    But also, yes, there is such composition operator/function defined in a library that is specifically aimed at writing point-free programs - compose2 and its friend compose2Flipped, aliased in operator form as <.. and ..> respectively:

    import PointFree ((..>))
    
    fn1 :: A -> B -> C
    fn2 :: C -> D
    
    fn4 :: A -> B -> D
    fn4 = fn1 ..> fn2
    

    Having said that, I strongly advise against using such composition. Point-free notation is neat and cool, but in practice it just makes your program incomprehensible. Much better just to write exactly what you mean:

    fn4 :: A -> B -> D
    fn4 a b = fn2 (fn1 a b)
    

    P.S. Here's a neat trick: how do you think I found that library? It's not like I have all the libraries and all their types and functions memorized, right?

    I used the Pursuit search. I knew the type signature of the function you seek would be (a -> b -> c) -> (c -> d) -> (a -> b -> d) and I just plugged that into search, and voila!

    Here, try it yourself: https://pursuit.purescript.org/search?q=(a+-%3E+b+-%3E+c)+-%3E+(c+-%3E+d)+-%3E+(a+-%3E+b+-%3E+d)