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

prep-5.0 #2

Merged
merged 15 commits into from
Jan 16, 2025
Merged

prep-5.0 #2

merged 15 commits into from
Jan 16, 2025

Conversation

inoas
Copy link
Owner

@inoas inoas commented Jan 16, 2025

No description provided.

@inoas
Copy link
Owner Author

inoas commented Jan 16, 2025

@jrstrunk Would you be so kind and could do a brief review. I'd be so glad.

Choose a reason for hiding this comment

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

I use this same convention! When I want to add functionality to a package, I create a new one in my project with an 'x' suffix, just like this 'optionx' module.

Just want to ask though, will this end up in given's public API?

Choose a reason for hiding this comment

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

I generated the docs and it does make it into the public API! Wouldn't we want this to be private?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I use this same convention! When I want to add functionality to a package, I create a new one in my project with an 'x' suffix, just like this 'optionx' module.

Just want to ask though, will this end up in given's public API?

I had it internal first. I can move it back there again.

Copy link
Owner Author

Choose a reason for hiding this comment

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

The x stands for extended... and I namespaced it into the package.

return consequence: fn() -> a,
else_return alternative: fn() -> a,
) -> a {
pub fn that(

Choose a reason for hiding this comment

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

I like this change for the qualified use case

@jrstrunk
Copy link

This looks really nice! The checks for empty lists are really nice, too.

The only thing I might ask to consider again is the inclusion of not functions. Unless I am mistaken, they don't seem to add any functionality; they just offer alternate semantics. I generally find negated conditions much more difficult to read, so I personally stay away from them.

Do you think there is a real need for the not functions, given that the user could just switch the return and else_return callbacks if needed?

With this release, I think the library is at a really good size—just enough functionality to be worth the dependency, but not so much that it takes a while to learn.

@inoas
Copy link
Owner Author

inoas commented Jan 16, 2025

The only thing I might ask to consider again

So I know from studying a bit of (Cognitive) Psychology but also practise that negation is bad if it can be avoided.

I am happy that Elixir is removing unless, for instance.

That being said, whenever you do want to reach for the negation I think going for the other label/callback is not what I would recommend. These labels are there for consistency and to give people options but not as a recommendation.

I still think it reads much better to write a not and then know the rest is negated.

use <- given.not(is_admin, else: fn() { noop() })

// long handling of how to ugrade a user to an admin

versus

use <- given.that(!is_admin, else: fn() { noop() })

// long handling of how to ugrade a user to an admin

I prefer the not-variants by far.

2 cents: I also think that gleam "should" have used and, aor, xor, nor and not instead of !, ||, && etc. The bang-negation is a common trap because it is easy to over-read and it is to close to the whole expression where the not introduces the whole thing to be negated. Much like in spanish a question or exclamation being lead by a vertically swapped question mark or exclamation mark.

So I removed some duplication and recommended unqualified imports but the not variants will stay. No one is forced to use them and they read much better than "unless" or "!" IMHO.

@inoas
Copy link
Owner Author

inoas commented Jan 16, 2025

As for the size, I do not plan to add more for now, as I also thought: I do not want to create a DSL.

@jrstrunk
Copy link

Since the args can be flipped, aren't these the same:

use <- given.not(is_admin, else: fn() { noop() })

// long handling of how to upgrade a user to an admin
use <- given.that(is_admin, return: fn() { noop() })

// long handling of how to upgrade a user to an admin

If they are the same, isn't the second preferable?

@inoas
Copy link
Owner Author

inoas commented Jan 16, 2025

Since the args can be flipped, aren't these the same:

use <- given.not(is_admin, else: fn() { noop() })

// long handling of how to upgrade a user to an admin
use <- given.that(is_admin, return: fn() { noop() })

// long handling of how to upgrade a user to an admin

If they are the same, isn't the second preferable?

The mind model I was following was that for boolean and list of boolean guards use should read as use given not use no args here.

The default case for booleans is return not else_return. So for booleans on the truthy branch the supplied label callback is executed and not the use-callback.

For Result and Option that's different: Here the recommended way to use when expecting "truthy" (Ok of Result) is in the use-callback instead of the supplied labeled callback.

The other labels are there to give people options if they desire to do so, like you seem to.

@inoas
Copy link
Owner Author

inoas commented Jan 16, 2025

Would you prefer given.non_empty or given.not_empty?

@jrstrunk
Copy link

Okay, then leaving it as is is fine with me! Users can choose how to use it. I like the given.non_empty name better personally.

@inoas
Copy link
Owner Author

inoas commented Jan 16, 2025

Okay, then leaving it as is is fine with me! Users can choose how to use it. I like the given.non_empty name better personally.

I am torn because the rest is named not and suddenly you need to use non, but non-empty-list is a well known term 🤷🏼

@jrstrunk
Copy link

Yeah, I understand that dilemma, haha. I still like "non-empty" because as you said it is a well-known term and for me it carries less mental overhead than "not empty."

@inoas
Copy link
Owner Author

inoas commented Jan 16, 2025

Yeah, I understand that dilemma, haha. I still like "non-empty" because as you said it is a well-known term and for me it carries less mental overhead than "not empty."

like not-empty could be a string, but non-empty revers to a list usually?

@inoas inoas merged commit 21cd082 into main Jan 16, 2025
1 check passed
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.

2 participants