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

isEmptyString & isNonEmptyString #40

Closed
char0n opened this issue Mar 8, 2017 · 21 comments
Closed

isEmptyString & isNonEmptyString #40

char0n opened this issue Mar 8, 2017 · 21 comments
Assignees
Labels
Milestone

Comments

@char0n
Copy link
Owner

char0n commented Mar 8, 2017

No description provided.

@char0n char0n added the feature label Mar 8, 2017
@char0n char0n self-assigned this Mar 8, 2017
@char0n
Copy link
Owner Author

char0n commented Mar 8, 2017

@tycho01 need some collaboration over this. Does this even make sense ? Thought I have one production use case in my current project.

const description = 'user input';
if (isNotNull(description) && isNotEmptyString(description)) { return description }

@KiaraGrouwstra
Copy link
Contributor

Sounds like just if (description) covers this use-case? That said, I think this whole function-based approach primarily shines if you're doing composition/map. That is, without need for a function one could technically be shorter off just writing description != '' for this.

But yeah, why not? :)

So I've personally tended toward higher abstraction, and wanted a slightly different truthy/falsy check for arrays/objects, here's what I've been using for that.

export let falsy: (v: any) => boolean = R.either(R.isEmpty, R.not);
export let truthy: (v: any) => boolean = R.complement(falsy); // differs from JS's 'truthy': []/{} -> false.

For other cases, in the case one would prefer higher abstraction, where you want a function version of the built-in truthy/falsy check, one could also use the built-in Boolean (/ R.identity) or R.not for positive/negative checks respectively.

@char0n
Copy link
Owner Author

char0n commented Mar 8, 2017

Sounds like just if (description) covers this use-case? That said, I think this whole function-based
approach primarily shines if you're doing composition/map. That is, without need for a function one
could technically be shorter off just writing description != '' for this.

if (description) would cover the usecase, but we have a airbnb codestyle where everything should be checked explicitly except for booleans. As for imperative over declarative system here, I like the first version better, I can read it faster and I know instantly what is going on there.

if (isNotNull(description) && isNotEmptyString(description)) { return description }`
if (description !== null && description !== '') { return description }

Also I like you falsy, truhy approach ;]

@KiaraGrouwstra
Copy link
Contributor

Yeah. I wish the built-in checks used this logic for truthy/falsy; it could make things a bit more terse when checking objects/arrays...

@char0n
Copy link
Owner Author

char0n commented Mar 8, 2017

Back to the original problem...it seems that isNotEmptyString doesn't make sense alone.

if (isString(description) && isNotEmptyString(description)) { return description }`

What makes more sense is

isNotEmptyString = both(isString, complement(isEmptyString));

@KiaraGrouwstra
Copy link
Contributor

allPass could be both for just two, for complement(isEmptyString) just Boolean could do, but yeah, whatevers. With composition like that you'll manage to avoid boilerplate, yeah.
P.S. if it includes isString I'd name it isNonEmptyString. :)

@char0n
Copy link
Owner Author

char0n commented Mar 8, 2017

isEmptyString = eq('');
isNonEmptyString = both(isString, complement(isEmptyString));

isNonEmptyString(null) -> false
isNonEmptyString('test') -> true
isNonEmptyString(udefined) -> false
isNonEmptyString('') -> false

Ok I guess it make sense now, thanks. Renamed to isNonEmptyString to distinguish that this is not just a simple complement.

@char0n
Copy link
Owner Author

char0n commented Mar 9, 2017

Conclusion: Implement two new type functions

// isEmptyString :: * -> Boolean
export const isEmptyString = equals('');

// isNonEmptyString :: * -> Boolean
export const isNonEmptyString = both(isString, complement(isEmptyString));

@tycho01 thanks for all your input.

@KiaraGrouwstra
Copy link
Contributor

Sure. :)

@char0n char0n changed the title isNotEmptyString isEmptyString & isNonEmptyString Nov 22, 2017
@char0n
Copy link
Owner Author

char0n commented Jan 17, 2018

@guillaumearm wanna take this one also ?

@guillaumearm
Copy link
Collaborator

guillaumearm commented Jan 19, 2018

Absolutely :-D
@char0n : I think you can assign this issue to v2.4.0, sorry for the late response, hard week for me
:-P

@char0n char0n added this to the v2.4.0 milestone Jan 19, 2018
@guillaumearm
Copy link
Collaborator

I'm on it ;-)

guillaumearm pushed a commit to guillaumearm/ramda-adjunct that referenced this issue Jan 20, 2018
@guillaumearm
Copy link
Collaborator

guillaumearm commented Jan 20, 2018

I have a problem with the proposed isEmptyString implementation above :

// isEmptyString :: * -> Boolean
export const isEmptyString = equals('');

@char0n : Should isEmptyString(new String('')) return true ?

Edit:
In fact, to be consistent with isNonEmptyString behavior, maybe should we use this implementations :

// isEmptyString :: * -> Boolean
export const isEmptyString = both(isString, isEmpty);

// isNonEmptyString :: * -> Boolean
export const isNonEmptyString = both(isString, isNotEmpty);

@char0n
Copy link
Owner Author

char0n commented Jan 20, 2018

IMHO it should return false for isEmptyString(new String('')). It is the case that in JavaScript '' !== new String('') so I would not worry about handling this usecase. They're just not the same.

Regarding the composition, I understand the consistency argument, but again in JavaScript it is case that '' === '' but {} !== {} and [] !== []. I don't think complex composition like both(isString, isEmpty) is needed for this usecase and simple equation should do. It is simple and quick, and I just like these two attributes. We should always strive to use the simplest workable solution.

If you look at the source of isEmpty, it calls empty on argument (monoid) and then doing the comparison using the equal...so basically it is doing the equals('', arg) which is what I am proposing.

@guillaumearm
Copy link
Collaborator

OK, it sounds good, but i have some trouble in isNonEmptyString tests because of isEmpty :

> R.isEmpty(new String('abc')) // => false
> R.isEmpty(new String()) // => false

so there is a problem here :

eq(RA.isNonEmptyString(new String('abc')), false); // test throw

@guillaumearm
Copy link
Collaborator

guillaumearm commented Jan 20, 2018

We can get around using a kind of isPlainString internal function in isNonEmptyString implementation.

const isPlainString = x => typeof x === 'string'

@char0n
Copy link
Owner Author

char0n commented Jan 20, 2018

What about something like this one ?

const isNonEmptyString = R.allPass([RA.isString, RA.isNotObj, RA.isNotEmpty]);

@guillaumearm
Copy link
Collaborator

guillaumearm commented Jan 20, 2018

I don't know.
Semantically speaking, I prefer your solution.

But according to you, which one is fastest ?

const isNonEmptyString = R.allPass([RA.isString, RA.isNotObj, RA.isNotEmpty]);

or

const isPlainString = x => typeof x === 'string';
const isNonEmptyString = both(isPlainString, isNotEmpty);

Need benchmark ?

@char0n
Copy link
Owner Author

char0n commented Jan 20, 2018

I'd prefer also the first one even though it is going to be slower. In the context of Equational Reasoning I prefer to compose new parts from the parts that are already available rather than create new add hoc parts just to satisfy the speed property. My main argument in previous comment was not about speed it self but the simplicity. Speed is secondary in this case. both(isString, isNotEmpty) can be translated directly into equals('') so we are basically creating a fushion showcut here because we understand the behavior of both and we know that they equal.

@guillaumearm
Copy link
Collaborator

Ok, so I go for the first solution, did you want I create 2 PRs ?

@char0n
Copy link
Owner Author

char0n commented Jan 20, 2018

yup thanks

guillaumearm pushed a commit to guillaumearm/ramda-adjunct that referenced this issue Jan 20, 2018
guillaumearm pushed a commit to guillaumearm/ramda-adjunct that referenced this issue Jan 20, 2018
guillaumearm pushed a commit to guillaumearm/ramda-adjunct that referenced this issue Jan 21, 2018
guillaumearm pushed a commit to guillaumearm/ramda-adjunct that referenced this issue Jan 21, 2018
guillaumearm pushed a commit to guillaumearm/ramda-adjunct that referenced this issue Jan 21, 2018
guillaumearm pushed a commit to guillaumearm/ramda-adjunct that referenced this issue Jan 21, 2018
char0n pushed a commit that referenced this issue Jan 21, 2018
@char0n char0n closed this as completed in cd94a5f Jan 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants