NonEmptyList
grossbart opened this issue ยท 7 comments
I used funkia/list on a larger project and was generally very happy with it. One big missing piece for me was the availability of a NonEmptyList. Having this available helps reduce a lot of code concerned with checking if a list is empty or not, because in many situations an empty list would be an application error and should be caught far out in the API layer, not in the app's core code.
The fp-ts NonEmptyArray is a good starting point for how this should work.
I'm happy to hear that you've enjoyed using List ๐
Adding a NonEmptyList
is something that I've been considering and something that I think would be very useful. However, I can see two different ways to implement it, and I'd like to hear what you think about them:
1/ Make NonEmptyList
s a seperate from List
and create a different version of relevant functions that only takes NonEmptyList
s. This is what I've seen done in languages like Haskell and PureScript.
2/ Make NonEmptyList
a subtype of List
. Then every function that accepts a List
also accepts a List
. But the type of relevant functions would change. For instance append
would return a NonEmptyList
and head
would be overloaded with the type
function head<A>(l: NonEmptyList<A>): A;
function head<A>(l: List<A>): A | undefined;
My current thoughts are that the latter approach the best fit for TypeScript. The major downside seems to be that the types of various function will be more complicated to handle NonEmptyList
s as well.
What do you think?
I was thinking way too long about this, there's always a con that crops up when you have so many pros for one or the other way ๐
In the end, I think your suggestion 2 makes the most sense for this library: it's a self-contained library that has the luxury to know all the types it needs to work with and can therefore provide functions that know how to deal with either List
or NonEmptyList
.
@grossbart Thank you for the feedback. It's the option I'm leaning towards as well. This shouldn't be too tricky to add. I'll work on it when I get the time and do a PR.
FWIW, fp-ts
follows approach 1, mimicking what languages like Haskell & PureScript do:
https://github.com/gcanti/fp-ts/blob/master/src/NonEmptyArray.ts
I like this, because it keeps the two types totally isolated and distinct from each other.
EDIT: Whoops, didn't notice NonEmptyArray from fp-ts was linked at the top already. ๐
@joshburgess I know they separate the two completely, but I don't have the intuitions yet to decide whether combining them for this library would mean trouble. My reasoning was that because this library is self-contained it couldn't hurt to have NonEmptyList
be a subtype of List
with an additional constraint. But maybe this is a fallacy and we trade comfort at the expense of lawfulness? I can certainly see that keeping them separate makes it more clear that they are different things (even though they seem so similar at first glance).
What about, instead of subtyping, just importing and using the utility functions from List
also with NonEmptyList
and casting? (internally only, I mean)
I'm no fan of casting, in general, but it would be only be an internal implementation detail. With this approach, it would allow reusing the existing code + making maintainability for the two separate list types easier, but would still be type-safe from the outside, requiring the user to use the proper types in their app code.
Just an idea. I would probably resort to something like that, I think... to cut down on maintenance costs, but also try to control how people use the API.
Then again, some utility functions for List might not really apply to NonEmptyList since the possibility of the list being empty is no longer there (this would change the implementation for some functions).
What about, instead of subtyping, just importing and using the utility functions from
List
also withNonEmptyList
and casting? (internally only, I mean)
That sounds interesting. But, I don't fully understand what you're proposing here? Could you maybe explain it in more detail?