Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helper for creating a new function via partial application #36

Closed
albrow opened this issue Mar 1, 2016 · 5 comments
Closed

Helper for creating a new function via partial application #36

albrow opened this issue Mar 1, 2016 · 5 comments

Comments

@albrow
Copy link

albrow commented Mar 1, 2016

I ran into a minor issue with the error messages generated by sanctuary def when you export a function that has already been partially applied.

For example, if we have a library that exports an incr function via a partial application of add like so:

const add =
def('add', {}, [$.Number, $.Number, $.Number], (x, y) => x + y);

exports.incr = add(1);

And a separate library imports it and calls incr(1, 2), sanctuary-def will throw a TypeError complaining that add expected two arguments but got three. This can be confusing because 1) the add function was never called directly and 2) the incr function expects only one argument, but the error message complains about a function that expects two arguments.

We could rewrite our incr like so:

exports.incr = def('incr', {}, [$.Number, $.Number], add(1));

For this simple case it's not so bad, but I was recently working on a library with 10-12 functions that were exported this way, each with different types of arguments. The boilerplate can become annoying and gets hard to maintain if you want refactor a function signature later (because now you have to change it in two places).

It would be awesome if sanctuary-def provided a helper function for defining a new function that is implemented as a partial application of another. It might look something like this:

// redef :: String -> Function -> Function
exports.incr = redef('incr', add(1));

redef takes a new name, a function which is returned by a partial application of some other function defined with def, and returns a new, curried function which accepts any arguments not already provided.

So now if you called incr(1, 2), the error message would explain that incr expects one argument but received two.

I'm not sure exactly what to call this helper function or if sanctuary-def has enough information to do the introspection that would be required here, but it would be extremely nice to have.

@davidchambers
Copy link
Member

I agree that this would be marvellous! I'll give the matter some thought.

@davidchambers
Copy link
Member

It would be nice to support aliases as well. Sanctuary, for example, will soon export both S.map and S.lift, despite these being the same function. It would be nice to be able to reuse the type-class constraints and type definition but still have the correct function name appear in the type signature.

@Koleok
Copy link

Koleok commented Nov 29, 2016

👍 on this all the way, I have been re-writing a LOT of type signatures to the point that I end up resorting to things like

const baseSig = [$.String, $.String]

// then later...

def('someFunction', {}, [...baseConfig, $.Boolean], implementationStuff)

@davidchambers
Copy link
Member

See also #39, @Koleok. :)

@davidchambers
Copy link
Member

I would now write the original code as follows:

//    add :: Number -> Number -> Number
const add = x => y => x + y;

exports.add =
def ('add')
    ({})
    ([$.Number, $.Number, $.Number])
    (add);

exports.incr =
def ('incr')
    ({})
    ([$.Number, $.Number])
    (add (1));

The implementation is shared yet type checking only occurs at the module boundary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants