-
Notifications
You must be signed in to change notification settings - Fork 78
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
Implement zip on NonEmptyList #40
Comments
- add test - closes fsprojects#40
- add test - closes fsprojects#40
Hi. I've made a pull request implementing zip. I'm wondering, wouldn't it be also nice to have some more conversion functions, such as ofSeq, ofList, ofArray; maybe also toSeq (besides the implicit cast)? |
About ofSeq, ofList, ofArray : the only way to implement them safely would be as e.g. |
Hi @mausch, I've responded to your review of my pull request there.
I mean something like: let l = [1;2;3]
let nel = NonEmptyList.ofList l rather than: let nel = NonEmptyList.create (List.head l) (List.tail l) (Note that the second, verbose version will raise an exception just as well if the list is empty.) |
The way I recommend to create a NonEmptyList from a list is: let nel =
match l with
| x::xs -> NonEmptyList.create x xs
| [] -> // handle that however you like Which is basically equivalent to 'a list -> 'a NonEmptyList option`. These partial functions are the cause of lots of bugs... Many people even suggest removing them altogether from standard libraries. Of course, as you say F# and even FSharpx already has lots of partial functions. But shouldn't we try to avoid adding even more, especially to a foundational library like FSharpx? |
I share the sentiment against partial functions. It's just a matter of being consistent with the standard, but leaving out such functions is a valid option. I do think that functions that return an But this thread started with
I think that the choice here will also reflect on the other functions that were mentioned. |
- add test - closes fsprojects#40
Thank you, @forki, for merging the pull request. Now, what about the conversation I had with @mausch? There's the question of This can get into a new issue, but it seems to me that the a similar logic should be applied to For compatibility, the two solutions can be merged: with |
Let's just follow the pattern set by FSharp.Core. Which means that this is ok as |
OK. So we'll leave it at that. Thanks for your answer. I'd also be happy to have such a library, if you ever come around to it. |
Bottom line: I think it's "ok" to have partial |
Oh, I thought you wanted to pass the burden of writing them to the client. |
Yes |
A bit off topic, I know ;) Just wanted to point out that total functions returning options have also their limitation in real world scenarios and now I notice they are starting to populate the F# core library, typically named tryFunctionName and also there are many already in the .NET framework returning an output parameter which in the end is isomorphic to the option type. The problem is that they tell you that something went wrong but not what, whereas those that fail tell you exactly what and where the problem was.
A better approach would be to return a I worked in a project where we started with exceptions but because the calculations were very likely to fail we moved to options but the users wanted to know what was wrong, so we went for the Fortunately I see also this pattern emerging in F# code and the convention seems to be to use a type alias of Choice as Result of Success or Failure, though no convention on the name of the functions (would be nice to have one). |
Interesting point. Well, I was looking again at Scott Wlaschin's "railway oriented programming" that you seem to be tacitly referring to. At first I wanted to say that with the railway approach you would name the functions normally and treat the So basically, a canonical Success/Failure function (trying to do something that might fail) is a "validateF", whereas any other function that is prone to raise an exception (coming from the impure non-functional world) can be called with let tryCatch f = try f |> Success with ex -> Failure ex used as an adapter. I guess that in the parsing example, you could have a So there seems to be a naming convention emerging here: "try-" = Option, "validate-" = Choice<'TSuccess, 'TFailure>, and no prefix is often a sign of a partial function that would require wrapping. |
Indeed I wasn't referring to that article, but just had a look and it seems to be aligned with my thoughts. Also found interesting there the discussion in the comments about using a DU for the failure and then transforming it as it travels different layers. But I think the validate convention is a particular case. In my case I was working with math functions which calculate-or-fail but they didn't validate anything at all. Take for instance the usual example of the total division, in the Haskell community seems to be an implicit agreement in using the word 'safe', here in .NET probably would be named tryDivide but then would be expected to return an option, so how would you call the (most interesting) version that returns a wrapped error in a Result?
Hopefully I'm wrong but I think there is no naming convention at all and I think is unfortunate that in .NET world the tryF naming convention is being used for the (less interesting) option resulting functions since |
Well, a division has only one possible way to fail, right? What additional information will you gain from the exception? In this case, I don't really see the added value of not using an option type (and calling it I'm wondering what kind of math functions may fail upon calculation in different ways, where the exceptions bear interesting information. Could you give an example? |
Hmm the division operator, as defined in C#/F# yes, but that's not a true division, it's rather an arbitrary mix of 2 different functions depending on which type operates.
The true division also exists for integers and it's closed only on those values where the dividend is multiple of the divisor. To continue with the inconsistencies just want to mention that the modulo operator acts as integer modulo also for floats as if the division there was the Euclidean instead of the true division (???) Anyway, it was just a trivial example but to answer your question I was using functions that might fail in different ways, for example a derivative computed by Newton-Raphson can return a value, diverge, not converge after maxIterations or eventually reach the limits of the type precision (0 or infinite). Still in the case of a function with a single point of failure (as the funny In the end what we will be doing is making an effect (in this case the exception) explicit in the type. F# and .NET Guidelines suggest that you should do this when the Exception is not really exceptional but where's the limit? In my case for sure it was not exceptional, less than 90% of the user calculations produced a value everyday because of numerical reasons. And what about the case discussed here: the empty list, is it exceptional? Do the Guidelines suggest to use partial functions? I'm afraid the answer is yes. Finally, I don't think the try functions returning options are useless, they make sense when the action is to do something else but not to abort reporting an error. I'm sure after some time when try functions are everywhere we will see more ... (what's the convention?) ... Choice-functions emerging. |
I too prefer Choice to Options in my app-level code. I tend to return Choice values when the function can have multiple reasons to fail... which is not the case of a function |
Yes, I totally agree, but my point was more in the scenario of a base library than in an application. |
In the comments of the "railway" article, Scott Wlachsin suggests using many different DUs for the different use-cases and transforming them as they travel along. This design (if you think it makes sense) means that there is no one-size-fits-all return type. Even if a library sets a DU describing the possible failures, it would have to be transformed when consumed by the client code into the DUs used by the client in that domain. It seems like going a full circle: wouldn't it make sense to simply let the library functions raise exceptions and then catch and wrap them in the client code into the proper case of the client's DU? Unless, of course, there's only one possible way a function may fail, in which case an option type is standard enough. |
The
NonEmptyList
type currently does not have azip
implementation. This comes in handy quite often.The text was updated successfully, but these errors were encountered: