Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

How to write a function that only accepts a list of `Error string` `Results` in F# on the level of types?

+2
−0

For example, given a mergeErrors function where input is always a list of Error strings,

let es = [ Error 1; Error 2; Error 3 ]

let mergeErrors<'a> (errors: Result<'a,int> list) : Result<'a,int list> =
    errors
    |> List.map (function | Error e -> e)
    |> Error

mergeErrors (es: Result<int,int> list)
//=> Error [1; 2; 3]

this will always blow up during runtime. Was wondering if there is a way to force failure to compile time?


Context

In my script, there is a Result<'a, string> seq seq type where each constituent Result<'a, string> seq needs to be flattened:

  • if there are Errors, then Oks can be discarded, and an Error concatenatedStrings should be returned
  • if all is Ok, then they are merged depending of the shape of 'a

My naive solution is to

let filterError (row: Result<'a, string> seq) =
    row 
    |> Seq.filter (function
        | Error _ -> true 
        | _       -> false)
    
let mergeErrors inputFromFilterError =
    // ...

This is almost surely the wrong approach (and found the F# For Fun and Profit's "Map and Bind and Apply, Oh my!" series while researching for an answer), but I'm still curious.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

1 answer

+2
−0

I'm pretty sure the answer is "no", especially in some reasonable way. If Result was defined in some object-oriented way, i.e. as an interface with Ok and Error being implementations of that interface, or if this was O'Caml and Result had been defined as a polymorphic variant, then you could do this. That said, the openness of these approaches would be inappropriate for Result.

Defined as a discriminated union, you can't accomplish this. However, the "right" way to handle this, which is roughly equivalent to the above approaches, but with explicit rather than implicit conversions, is not to put the strings into the Result type in the first place. Or, in your case, avoid boolean blindness, a term popularized (and coined?) in this blog post, but search engines will return many other more recent discussions.

Instead of discovering which case of the Result you have with the pattern match in filterError and then immediately discarding that type information by returning a bool, you could instead have a function fromError : Result<'a, 'e> -> Option<'e>, this maintains the type information. Your filterError function would then become Seq.collect (fromError >> Option.toList).

If we adapt to the new type of filterError, then applying the same aversion to discarding type information to mergeErrors would lead to it simply being the identity function.

In general, uses of bool should be considered skeptically, and you should see if a bool-producing function can be replaced with a function that gives evidence for the result it returns. Functions like Seq.isEmpty, Option.isSome, Option.isNone, Result.isError, Result.isOk, etc. are almost never what you want. They and booleans in general tend to lead to implicit contracts between parts of your code. That this contract is implicit is part of the "blindness" of boolean blindness. Replacing bool returning functions with Option or Choice returning functions is often a solution.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »