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

feat: concatAll #404

Merged
merged 10 commits into from
Feb 24, 2018
Merged

feat: concatAll #404

merged 10 commits into from
Feb 24, 2018

Conversation

wojpawlik
Copy link
Contributor

Resolves #109.

Copy link
Owner

@char0n char0n left a comment

Choose a reason for hiding this comment

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

Thank you. Nice work! There are couple of issues we need to solve before merging.

src/index.d.ts Outdated
* Returns the result of concatenating all semigroups (such as arrays or strings) in passed foldable (such as an array).
* Returns null if empty foldable was passed.
*/
concatAll<S extends Semigroup>(foldable: Foldable<S>): S | null;
Copy link
Owner

Choose a reason for hiding this comment

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

@BjornMelgaard can you review this pls ?

src/concatAll.js Outdated
* @sig Semigroup s => Foldable s f => f -> (s | null)
* @param {Foldable<Semigroup>} foldable foldable with semigroups to concatenate
* @return {Semigroup|null} concatenated semigroups, or null, if empty foldable was passed
* @see {@link http://ramdajs.com/docs/#defaultTo|defaultTo}
Copy link
Owner

Choose a reason for hiding this comment

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

why defaultTo ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because it's a way to get rid of the returned null.

Copy link
Owner

Choose a reason for hiding this comment

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

Yes but it's just an implementation detail. Consumer of the api should not be conserned with that. Actually I'm more inclined to use undefined instead of null. I've used some ramda functions today and noticed the ramda is never returning null. Always undefined.

http://ramdajs.com/docs and search for | undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree with undefined.

Well, actually the user of it should be aware of concerned that it'll return null or undefined when it receives empty foldable.

Copy link
Owner

Choose a reason for hiding this comment

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

Yes but link to defaultTo using @see doesn't do the job. Actually I think that there should only bee two @see tags with reference to R.concat and RA.concatRight. We should also add @see to concatRight that referece to RA.concatAll.

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 don't think it's necessary to reference RA.concatRight.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, I'm of two minds about whether or not @see R.unnest and R.join, from one perspective, concatAll does a similar thing, except it has no sensible return value for empty foldable. From the other, R.unnest and concatAll have significantly different requirements about their parameters (Chain vs Foldable Semigroup).

eq(concatAll(['1', '2', '3']), '123');
});

it('returns null if empty foldable was passed', function() {
Copy link
Owner

Choose a reason for hiding this comment

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

Your tests are failing on some ramda version. Ramdas utilities become fantasy land compatible in some specific version. We have a pattern of future detection in tests.

See here

if (isFantasyLandSupported) {

});

it('throws if non-foldable is passed', function() {
assert.throws(() => concatAll(null), TypeError);
Copy link
Owner

Choose a reason for hiding this comment

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

I don't see any tests with Semigroups.

Use RA.Identity or monet to create couple of tests; see here

eq(RA.notBoth(Just(true), Just(true)), Just(false));

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 think I'll test it on NonEmptyList

Copy link
Owner

Choose a reason for hiding this comment

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

Try I'm looking forward to see it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

src/concatAll.js Outdated
* @since {@link https://char0n.github.io/ramda-adjunct/2.6.0|v2.6.0}
* @category List
* @sig Semigroup s => Foldable s f => f -> (s | null)
* @param {Foldable<Semigroup>} foldable foldable with semigroups to concatenate
Copy link
Owner

Choose a reason for hiding this comment

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

Foldable<Semigroup> is not true for all version of ramda we are supporting.
I'd go for the following just like R.concat do

@sig [a] -> [a] -> [a]
@sig String -> String -> String

And add a sentence: Can also concatenate multiple members of a fantasy-land compatible semigroup.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Partly done, added the signatures, but didn't remove the previous one.

src/concatAll.js Outdated
const nullSemigroup = { [fl.concat]: identity };

/**
* Returns the result of concatenating all semigroups (such as arrays or strings) in passed foldable (such as an array).
Copy link
Owner

Choose a reason for hiding this comment

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

Returns the result of concatenating the given lists or strings.
Note: R.concat expects all items to be of the same type. It will throw an error if you concat an Array with a non-Array value.
Dispatches to the concat method of the item, if present. Can also concatenate multiple members of a fantasy-land compatible semigroup.

Copy link
Owner

Choose a reason for hiding this comment

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

We should also add info about returning undefined. It's missing in this description. Please do so.

import eq from './shared/eq';

describe('concatAll', function() {
it('concatenates arrays', function() {
Copy link
Owner

Choose a reason for hiding this comment

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

Prefer should notations; the same applies for other tests

it('should concatenate arrays'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/concatAll.js Outdated
import stubNull from './stubNull';
import fl from './fantasy-land/mapping';

const nullSemigroup = { [fl.concat]: identity };
Copy link
Owner

Choose a reason for hiding this comment

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

I guess we'll need to change this to be compatible from ramda 0.19.1 to ramda 0.25.0

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already done. Changed back to 'concat'.

src/concatAll.js Outdated
* @sig Semigroup s => Foldable s f => f -> (s | undefined)
* @param {Foldable<Semigroup>} foldable foldable with semigroups to concatenate
* @return {Semigroup|null} concatenated semigroups, or undefined, if empty foldable was passed
* @see {@link http://ramdajs.com/docs/#defaultTo|defaultTo}
Copy link
Owner

Choose a reason for hiding this comment

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

Please drop defaultTo and add RA.concatRight

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remove or leave those in?

/*
 * @see {@link http://ramdajs.com/docs/#unnest|unnest}
 * @see {@link http://ramdajs.com/docs/#join|join}
 */

Copy link
Owner

Choose a reason for hiding this comment

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

You explained these two in prev comments and I guess it make sense to keep them.

src/concatAll.js Outdated

import stubUndefined from './stubUndefined';

const nullSemigroup = { concat: identity };
Copy link
Owner

Choose a reason for hiding this comment

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

Null semigroup may be incorrect name for this. Shouldn't we go for something like leftIdentitySemigroup ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done (but not yet pushed, but it's still useful to mark it as done for me)

src/concatAll.js Outdated
const nullSemigroup = { concat: identity };

/**
* Returns the result of concatenating all semigroups (such as arrays or strings) in passed foldable (such as an array).
Copy link
Owner

Choose a reason for hiding this comment

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

You're being too academic on this. This is not the way how Ramda documents it's api. So again I propose the following:

Returns the result of concatenating the given lists or strings.
Note: RA.concatAll expects all elements to be of the same type. It will throw an error if you concat an Array with a non-Array value.
Dispatches to the concat method of the preceding element, if present. Can also concatenate multiple elements of a [fantasy-land compatible semigroup](https://github.com/fantasyland/fantasy-land#semigroup).
Returns undefined if empty array was passed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right.

Returns undefined if empty array was passed.

Should I mention R.defaultTo there somehow? "pipe the result through R.defaultTo to replace the undefined with something useful"

The best wording I came up with.

Copy link
Owner

Choose a reason for hiding this comment

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

Nope we should not do that. For me it's the same as using R.find function with signature (a → Boolean) → [a] → a | undefined. They don't tell us there what to do with undefined.

Some functions just returns undefined, as an expected output. I trying to understand your reasoning but I don't see it, I am sorry. Should we also mention defaultTo when documenting R.prop function or R.nth ? I think not.

src/concatAll.js Outdated
* @memberOf RA
* @since {@link https://char0n.github.io/ramda-adjunct/2.6.0|v2.6.0}
* @category List
* @sig [[a]] -> ([a] | undefined)
Copy link
Owner

Choose a reason for hiding this comment

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

Drop the redundant parens and use Undefined to denote a Category not a value

[[a]] -> [a] | Undefined

src/concatAll.js Outdated
* @since {@link https://char0n.github.io/ramda-adjunct/2.6.0|v2.6.0}
* @category List
* @sig [[a]] -> ([a] | undefined)
* @sig [String] -> (String | undefined)
Copy link
Owner

Choose a reason for hiding this comment

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

Drop the redundant parens and use Undefined to denote a Category not a value

[String] -> String | Undefined

src/concatAll.js Outdated
* @sig [String] -> (String | undefined)
* @sig Semigroup s => Foldable s f => f -> (s | undefined)
* @param {Foldable<Semigroup>} foldable foldable with semigroups to concatenate
* @return {Semigroup|null} concatenated semigroups, or undefined, if empty foldable was passed
Copy link
Owner

Choose a reason for hiding this comment

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

@return {Array|string|undefined} Concatenated elements

assert.throws(() => concatAll([1, 2, null, true]), TypeError);
});

it('should throw if foldable contains non-compatible semigroups', function() {
Copy link
Owner

Choose a reason for hiding this comment

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

This test is not passing on ramda 0.23.0

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I remove it then?

Copy link
Owner

Choose a reason for hiding this comment

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

We have to find out why it's not passing and do feature detection to enable/disable test depending on features R provides for us in particular version

Copy link
Contributor Author

@wojpawlik wojpawlik Feb 24, 2018

Choose a reason for hiding this comment

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

Ramda 0.23 just doesn't throw for concating string and array, so I changed the test to try to concat array and string ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This fails in 0.21 tho!

I think I'll just remove the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ramda should be testing that in R.concat anyway.

Copy link
Owner

Choose a reason for hiding this comment

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

Just comment the test out, don't remove it. I'll play with that after merge.

assert.throws(() => concatAll(null), TypeError);
});

it('should throw if foldable contains non-semigroups', function() {
Copy link
Owner

Choose a reason for hiding this comment

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

Your test description contains branching logic. We use more proper mechanism for this.

context('when foldable contains non-semigroups', function () {
  specify('should throw error', function () {
    assert.throws(() => concatAll([1, 2, null, true]), TypeError);
  });
});

Ref:

context('when the first function returns false', function() {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. It looks so neat now. Thanks, I have no experience in testing.

src/index.d.ts Outdated
* Returns the result of concatenating all semigroups (such as arrays or strings) in passed foldable (such as an array).
* Returns null if empty foldable was passed.
*/
concatAll<S extends Semigroup>(foldable: Foldable<S>): S | null;
Copy link
Owner

Choose a reason for hiding this comment

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

concatAll<S extends Semigroup>(foldable: Foldable<S>): S | undefined;

@@ -376,6 +385,12 @@ declare namespace RamdaAdjunct {
*/
ensureArray<T>(value: T | T[]): T[];

/**
Copy link
Owner

Choose a reason for hiding this comment

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

Don't forget to align this description with the one I proposed in previous comment

import { assert } from 'chai';
import { NEL, Nil } from 'monet';

import { concatAll } from '../src';
Copy link
Owner

Choose a reason for hiding this comment

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

Use following notation in tests pls.

import * as RA from '../src';

Ref:

import eq from './shared/eq';

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do. Why tho?

Copy link
Owner

Choose a reason for hiding this comment

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

It's a convention we use through out our tests to distinguish between R and RA functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@wojpawlik
Copy link
Contributor Author

Oh ffs, I thought CI would pass it by now

@wojpawlik
Copy link
Contributor Author

Finally.

If everything's fine, merge-squash it or something, don't rebase all of the commits on master, that'd be messy.

If you don't want to merge-squash, I can rebase-squash and force-push.

@guillaumearm
Copy link
Collaborator

@GingerPlusPlus : that's fine, here is "squash and merge" for each PRs.

@wojpawlik
Copy link
Contributor Author

@guillaumearm The thing is, I don't know who does it show as the creator of the merged commit

@guillaumearm
Copy link
Collaborator

@GingerPlusPlus : don't worry about that.
Your RA.omitBy commit has been squashed : c7c9c7e

@char0n char0n merged commit 341f3f1 into char0n:master Feb 24, 2018
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.

3 participants