the semantical and resourcewise equivalent would be a third variable
So you are advocating for:
data class Filters(
val isAdmin: Boolean = false,
val filterByAdmin: Boolean = false,
val isConfirmed: Boolean = false,
val filterByConfirmed: Boolean = false,
)
fun filterUsers(users: List<User>, filters: Filters): List<User> {
return users
.filter { !filters.filterByAdmin || it.isAdmin == filters.isAdmin }
.filter { !filters.filterByConfirmed || it.isConfirmed == filters.isConfirmed }
}
fun getAdmins() {
val users = getUsers()
val filters = Filters(isAdmin = true, filterByAdmin = true)
val admins = filterUsers(users, filters)
println("Admins: $admins")
}
Over:
data class Filters(
val isAdmin: Boolean? = null,
val isConfirmed: Boolean? = null,
)
fun filterUsers(users: List<User>, filters: Filters): List<User> {
return users
.filter { filters.isAdmin == null || it.isAdmin == filters.isAdmin }
.filter { filters.isConfirmed == null || it.isConfirmed == filters.isConfirmed }
}
fun getAdmins() {
val users = getUsers()
val filters = Filters(isAdmin = true)
val admins = filterUsers(users, filters)
println("Admins: $admins")
}
To me, Filters(isAdmin = true) is a very easy to use API, where Filters(isAdmin = true, filterByAdmin = true), with the additional variable to avoid nullable booleans, is more verbose, for not much benefit and brings ambiguity. What if someone writes Filters(isAdmin = true), but forgets they need to set filterByAdmin = true for it to actually work? Easy mistake to make. We can prevent these mistakes by removing default values so they have to be specified on the call site, but then you need Filters(isAdmin = true, filterByAdmin = true, isConfirmed = false, filterByConfirmed = false), which is very verbose. Having two variables also allows your systems to get into invalid states:
What do these mean? It’s better for invalid states to be unrepresentable. Since these states are mutually exclusive, we should have only 3 states, not 4 which you get with 2 booleans. Which you could achieve with an enum True, False, None, but then you are just reinventing the wheel of nulls. You also get the issue that now you have to remember to always update both variables together.
It all comes back to your point:
it’d be idiomatic and reasonable to assume it to be false if we have no data
You want to have ambiguous states, where a single value represents both “we have no data” and “we have the data, the answer is no”.
And I’m not advocating for any of that. That’s just weird design, both of them, and as such a good example of something that warrants a bigger redesign in general.
Just advocating for clear, sensible, self-documenting and most importantly, expandable and maintainable code.
What’s idiomatic varies between languages and the conventions aren’t the same even then, when arguing across disciplines. This discussion seems to be more about different educations. I can get your point but from my personal experience in academia and working in the field it sounds undesired. But that’s just it. My, as in extremely limited, perspective. From your pov what you argue here is probably equally correct to what I think from mine is from my pov, it’s just a difference in the segment of the field we work in I suppose. Or plain old cultural differences.
Whichever it is, I bet we both can find better use for our time. I’m thankful for the time and effort though, even if I wasn’t persuaded. Sorry to have prolonged it so.
Then you’d need to do something else.
So you are advocating for:
Over:
To me,
Filters(isAdmin = true)
is a very easy to use API, whereFilters(isAdmin = true, filterByAdmin = true)
, with the additional variable to avoid nullable booleans, is more verbose, for not much benefit and brings ambiguity. What if someone writesFilters(isAdmin = true)
, but forgets they need to setfilterByAdmin = true
for it to actually work? Easy mistake to make. We can prevent these mistakes by removing default values so they have to be specified on the call site, but then you needFilters(isAdmin = true, filterByAdmin = true, isConfirmed = false, filterByConfirmed = false)
, which is very verbose. Having two variables also allows your systems to get into invalid states:isAdmin = true, adminRightsHaveBeenValidated = false
isAdmin = true, filterByAdmin = false
What do these mean? It’s better for invalid states to be unrepresentable. Since these states are mutually exclusive, we should have only 3 states, not 4 which you get with 2 booleans. Which you could achieve with an enum
True
,False
,None
, but then you are just reinventing the wheel of nulls. You also get the issue that now you have to remember to always update both variables together.It all comes back to your point:
You want to have ambiguous states, where a single value represents both “we have no data” and “we have the data, the answer is no”.
Precisely my point.
And I’m not advocating for any of that. That’s just weird design, both of them, and as such a good example of something that warrants a bigger redesign in general.
Just advocating for clear, sensible, self-documenting and most importantly, expandable and maintainable code.
What’s idiomatic varies between languages and the conventions aren’t the same even then, when arguing across disciplines. This discussion seems to be more about different educations. I can get your point but from my personal experience in academia and working in the field it sounds undesired. But that’s just it. My, as in extremely limited, perspective. From your pov what you argue here is probably equally correct to what I think from mine is from my pov, it’s just a difference in the segment of the field we work in I suppose. Or plain old cultural differences.
Whichever it is, I bet we both can find better use for our time. I’m thankful for the time and effort though, even if I wasn’t persuaded. Sorry to have prolonged it so.