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

Curry: Add functions curry2 through curry5 #289

Merged
merged 1 commit into from
Nov 21, 2016
Merged

Curry: Add functions curry2 through curry5 #289

merged 1 commit into from
Nov 21, 2016

Conversation

laduke
Copy link
Contributor

@laduke laduke commented Nov 19, 2016

Hi, first pull request here. Thanks for the hand-holding. This closes #277.
Things not complete or I'm not sure about:

  • Examples for curry3 - 5.
    Looks like all of the examples in the readme are one liners.
  • Documentation text is probably too short, but I'm not the one that should be explaining currying to anyone.
  • I probably skipped some test cases. Is there an S.__ or similar that needs to be tested?
  • The definition forvar e = $.TypeVariable('e'); is skipped and I don't know why. So curry4 and 5 don't have an 'e': S.curry4 = def('curry4', {}, [$.Function, a, b, c, d, f], curry4);
  • Not sure what variable names people traditionally use for functions of large arity.
    I have function(v, w, x, y, z)...
  • Should I make the README and commit, or does that happen later?

@davidchambers
Copy link
Member

Thanks for the pull request, @laduke! It's exciting to have received pull requests from three new code contributors in the past week!

I edited this pull request's description to include the substring closes #277 so the issue this pull request addresses will automatically be closed when this pull request is merged. See Closing Issues via Pull Requests for details. :)

@davidchambers
Copy link
Member

The definition forvar e = $.TypeVariable('e'); is skipped and I don't know why.

We simply haven't needed this so many unary type variables before, but we're free to define e. :)

Should I make the README and commit, or does that happen later?

The readme gets updated automatically at release time. See sanctuary-js/sanctuary-set#1 (comment) if you're interested in the details.

//# curry2 :: ((a, b) -> c) -> a -> b -> c
//.
//. Curry a binary function.
//. ```javascript
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include an "empty" (//.) line between the description and the code block.

//. ```javascript
//. > todo
//. todo
//. ```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an example which does not seem contrived:

> global.toUrl = S.curry5((protocol, creds, hostname, port, pathname) =>
.   protocol + '//' +
.   S.maybe('', _ => _.username + ':' + _.password + '@', creds) +
.   hostname +
.   S.maybe('', S.concat(':'), port) +
.   pathname
. )

> toUrl('https:')(S.Nothing)('example.com')(S.Just('443'))('/foo/bar')
'https://example.com:443/foo/bar'

> toUrl('https:', S.Nothing, 'example.com', S.Just('443'), '/foo/bar')
'https://example.com:443/foo/bar'

eq(typeof S.curry2, 'function');
eq(S.curry2.length, 3);

var curried = S.curry2(source);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's define the function here:

var curried = S.curry2(function(x, y) { return x * y; });

@@ -632,6 +632,54 @@

//. ### Function

//# curry2 :: ((a, b) -> c) -> a -> b -> c
//.
//. Curry a binary function.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like these short descriptions. For consistency we should use Curries a binary function. Also, we could be more specific about which function gets curried:

Curries the given binary function.

@laduke
Copy link
Contributor Author

laduke commented Nov 19, 2016

Thank you for your patience @davidchambers.

//. > global.createRect = S.curry4((x, y, width, height) => (
//. . {x, y, width, height}
//. . )
//. . )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer this formatting:

> global.createRect = S.curry4((x, y, width, height) =>
.   ({x, y, width, height})
. )

//.
//. ```javascript
//. > R.map(S.curry2(Math.pow, 10), [1, 2, 3])
//. [ 10, 100, 1000 ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use [10, 100, 1000] here (without the extra spaces).

//. createRect
//.
//. > createRect(0)(0)(10)(10)
//. { x: 0, y: 0, width: 10, height: 10 }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use {x: 0, y: 0, width: 10, height: 10} here (without the extra spaces).

@laduke
Copy link
Contributor Author

laduke commented Nov 19, 2016

I've become spoiled by jsfmt. Sorry about those.

eq(curried(1, 2, 3, 4, 5), 15);
eq(curried(1)(2)(3)(4)(5), 15);
eq(curried(1, 2)(3, 4)(5), 15);
eq(curried(1)(2, 3)(4, 5), 15);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might as well enumerate all the combinations. I wrote a script to make this easier:

'use strict';

const pow2 = exp => Math.pow(2, exp);

const generateAssertions = (xs, f) => {
  const l = xs.length;
  return Array(pow2(l - 1)).join('x').split('x').reduce((s, _, m) =>
    xs.reduce((s, x, n) =>
      `${s}${x}${n === l - 1 ? '' : m % pow2(l - n - 1) >= pow2(l - n - 2) ? ')(' : ', '}`,
      `${s}  eq(curried(`
    ) + `), ${f(...xs)});\n`,
    ''
  );
};

process.stdout.write(generateAssertions([1, 2, 3, 4, 5], (v, w, x, y, z) => v + w + x + y + z));
$ node generate-assertions.js
  eq(curried(1, 2, 3, 4, 5), 15);
  eq(curried(1, 2, 3, 4)(5), 15);
  eq(curried(1, 2, 3)(4, 5), 15);
  eq(curried(1, 2, 3)(4)(5), 15);
  eq(curried(1, 2)(3, 4, 5), 15);
  eq(curried(1, 2)(3, 4)(5), 15);
  eq(curried(1, 2)(3)(4, 5), 15);
  eq(curried(1, 2)(3)(4)(5), 15);
  eq(curried(1)(2, 3, 4, 5), 15);
  eq(curried(1)(2, 3, 4)(5), 15);
  eq(curried(1)(2, 3)(4, 5), 15);
  eq(curried(1)(2, 3)(4)(5), 15);
  eq(curried(1)(2)(3, 4, 5), 15);
  eq(curried(1)(2)(3, 4)(5), 15);
  eq(curried(1)(2)(3)(4, 5), 15);
  eq(curried(1)(2)(3)(4)(5), 15);

You could run this for each of the other 🍛 functions as well.

@davidchambers
Copy link
Member

We should make it clear that the functions are curried Ramda-style. I'd like to see two applications of each function. For example:

> toUrl('https:')(S.Nothing)('example.com')(S.Just('443'))('/foo/bar')
'https://example.com:443/foo/bar'

> toUrl('https:', S.Nothing, 'example.com', S.Just('443'), '/foo/bar')
'https://example.com:443/foo/bar'

function curry5(f, v, w, x, y, z) {
return f(v, w, x, y, z);
}
S.curry5 = def('curry5', {}, [$.Function, a, b, c, d, e, f], curry5);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to use r (result?) in place of f, because in another pull request f will become a unary type variable. In other words, its type will be Type -> Type rather than Type, and it will no longer be suitable in this context. Using a different type variable now will save me from having to change it in the other pull request.

@laduke
Copy link
Contributor Author

laduke commented Nov 20, 2016

Chippin' away. Thanks for that script.
I now see you wrote that curry5 example with 2 different ways of applying.

eq(curried(1, 2, 3), 6);
eq(curried(1)(2)(3), 6);
eq(curried(1, 2)(3), 6);
eq(curried(1)(2, 3), 6);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, let's use the script to generate the assertions for curry2 and curry3 as well. You have all the combinations, but my obsessive streak wants to see them consistently ordered. 😜

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also changed them to use + instead of * in the curried function. ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me. 👍

@davidchambers
Copy link
Member

Looking good, @laduke! After addressing the final comment could you squash the commits and rebase on master? We'll then be ready to merge, I think. :)

eq(curried(1)(2)(3, 4)(5), 15);
eq(curried(1)(2)(3)(4, 5), 15);
eq(curried(1)(2)(3)(4)(5), 15);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have an empty line after the last assertion? Sorry for the pedantry. No more requests, I promise!

Please use git commit --amend when making the change. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I deserve every word of it.

@davidchambers
Copy link
Member

🌳 Terrific first contribution! Thanks for all your work on this. :)

@davidchambers davidchambers merged commit 4421ef3 into sanctuary-js:master Nov 21, 2016
@laduke
Copy link
Contributor Author

laduke commented Nov 21, 2016

🎉

@davidchambers
Copy link
Member

$ npm install [email protected]

$ node
> const S = require('sanctuary')
undefined
> S.curry2
curry2 :: ((a, b) -> c) -> a -> b -> c
> S.curry3
curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d
> S.curry4
curry4 :: ((a, b, c, d) -> e) -> a -> b -> c -> d -> e
> S.curry5
curry5 :: ((a, b, c, d, e) -> r) -> a -> b -> c -> d -> e -> r

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

Successfully merging this pull request may close these issues.

curry2 :: ((a, b) -> c) -> a -> b -> c
2 participants