I've read in a book that the function signature of tap function (also called K-Combinator) is below:
tap :: (a -> *) -> a -> a
"This function takes an input object a and a function that performs some action on a. It runs the given function with the supplied object and then returns the object."
- Can someone help me to explain what is the meaning of star (*) in the function signature?
- Are below implementation correct?
- If all the three implementation are correct, which one should be used when? Any examples?
Implementation 1:
const tap = fn => a => { fn(a); return a; };
tap((it) => console.log(it))(10); //10
Implementation 2:
const tap = a => fn => { fn(a); return a; };
tap(10)((it) => console.log(it)); //10
Implementation 3:
const tap = (a, fn) => {fn(a); return a; };
tap(10, (it) => console.log(it)); //10
This looks much like the Ramda definition. The *
in there is probably a mistake. (Disclaimer: I'm one of the Ramda authors.) It should probably read
// tap :: (a -> b) -> a -> a
An implementation like your first one:
const tap = fn => a => { fn(a); return a; };
or Ramda's version:
const tap = curry((fn, a) => { fn(a); return a; });
match that signature and are useful IMHO mostly in debugging contexts. I use it to temporarily introduce logging statements into functional pipelines1:
// :: Map String User
const users = fetchUsersFromSomewhere();
// :: [Comment] -> [Number]
const userRatingForComments = R.pipe(
R.pluck('username'), // [Comment] -> [String]
R.tap(console.log), // for debugging, need to include `bind` in browser envs
// ^^^^^^^^^^^^^^^^^^
R.map(R.propOf(users)), // [String] -> [User]
R.pluck('rating') // [User] -> [Number]
);
This is really not the K combinator, though.
1 This code sample is from an old article of mine on Ramda.