_______               __                   _______
       |   |   |.---.-..----.|  |--..-----..----. |    |  |.-----..--.--.--..-----.
       |       ||  _  ||  __||    < |  -__||   _| |       ||  -__||  |  |  ||__ --|
       |___|___||___._||____||__|__||_____||__|   |__|____||_____||________||_____|
                                                             on Gopher (inofficial)
   URI Visit Hacker News on the Web
       
       
       COMMENT PAGE FOR:
   URI   Fault Report – and alternative to Result in F#
       
       
        akdor1154 wrote 5 hours 57 min ago:
        I'm pretty sure this is isomorphic to Go's err, right?
        
        Report ~ res, err (well this bit not strictly, but in practice close
        enough)
        
        IFault ~ Err (interface over a string repr, and a way to wrap a cause
        Err with err.Unwrap)
        
        In my experience if you land on a design that looks like something
        either Go or F# does then you're probably on the right track. :)
       
          gf000 wrote 3 hours 48 min ago:
          > In my experience if you land on a design that looks like something
          either Go or F# does then you're probably on the right track
          
          Well, let's just say that our experiences are vastly different then,
          but I had to do a double take when I saw Go mentioned as a positive
          from a PL design perspective..
          
          And no, Go can literally return both a value and an error at the same
          time, it has possibly the worst error handling out of any "modern"
          language.
       
          neonsunset wrote 5 hours 3 min ago:
          Go lets you ignore the errors and does not have tuples - only
          multi-variable returns so you can't not deconstruct the return value
          (and can't .map it as a result too).
          
          I don't think it's fair to place the two next to each other.
          
          F# also does exception handling (where it makes sense, though
          TryParse is more efficient):
          
            let values = ["abcd"; "1234"]
            let number = try int values[0] with _ -> 1337
       
        EdwardDiego wrote 5 hours 59 min ago:
        It's been a very long age since I wrote any F#, really enjoyed it at
        the time and it was for fun stuff, I wasn't trying to deal with errors,
        just things like process a prefix trie in a cool way, but does
        
          Result<'T, 'TError> 
        
        really have no upper type bounds on TError?
        
        I'm assuming that's the case from the fact that he's arguing for a
        basic interface (Does F# support structural typing, or do you need to
        say that AtlasError implements IError or whatevs?), and I'm really
        surprised there isn't one.
        
        Huh, would love to know why leaving it unbounded was decided upon.
        Might be I don't quite grok the culture of F#.
        
        This reminds me of a very common and awful Python pattern, where, as
        there's no `message` attribute on the base error/exception classes,
        despite every nearly every exception or error having one, this is the
        normal pattern to access it:
        
          message = ex.args[0]
        
        It gets really fun when you get an IndexError because one exception had
        no message provided when instantiated, so then you find code like:
        
          message = ex.args[0] if ex.args else None
        
        Yech.
       
          smoothdeveloper wrote 3 hours 31 min ago:
          /!\ this is just my perspective /!\
          
          > would love to know why leaving it unbounded was decided upon. Might
          be I don't quite grok the culture of F#.
          
          You may look at more context in the discussions pertaining to the
          RFC: [1] I think those are the main factors:
          
          * "Keep It Simple Stupid" principle
          
          * community pressure so that F# libraries could standardise on better
          things that Choice1Of2 = Ok and Choice2Of2 = Error, or using a bare
          tuple (which is very not clean for APIs)
          
          * F# ought to remain flexible in terms of not pretending of totally
          abstracting away the fact that most of the dotnet base class library
          or the overall dotnet ecosystem, uses exceptions, it is not like
          Result type was created to abstract runtime exceptions away.
          
          For all other needs, one is just one wrapper / or abstraction (for
          library that don't "meet our standards" in terms of exception safety)
          away, along with a bit of adhoc tooling around leveraging
          FSharp.Compiler.Service for sake of adding static analysis, for
          architecture astronauts, dealing with behemoth or safety critical
          codebases, requiring even more screws turned, but even there, F# (and
          so many other languages) may not meet the bar if those are the
          critical properties for a project involving code.
          
          Overall, F# is just pragmatic for the 99.99%, and not trying too hard
          beside the steady core language idioms, and how the organic community
          wants it over time, to satisfy hypothetical and intellectual pursuits
          of the highest abstraction and safety.
          
          The culture of F# is mostly about throwing productivity parties and
          being done with it (while not suffering so many of the wrong choices
          done by C, C++, Java and it's famed sibbling, C#), rather than the
          higher conceptual type theory perfection that (sometimes ?) lead to
          code that is not less kludgy, or easier to maintain, in the grand
          scheme, for the masses of developers, that are not yet expert in
          Haskell, Scala, etc.
          
          It is probably not too dissimilar to OCaml culture, while embracing
          some aspects that are familiar to more people, due to relationship
          with dotnet ecosystem.
          
   URI    [1]: https://github.com/fsharp/fslang-design/blob/main/FSharp-4.1...
       
            neonsunset wrote 2 hours 0 min ago:
            C# is not a Java sibling :(
            
            (There are languages more similar to C# than Java and vice versa,
            and of course the underlying type system differences between JVM
            and .NET also add to this rift, C# was intended as a successor to
            C++ just as much as it was to Java)
       
              psd1 wrote 1 hour 46 min ago:
              They are not identical twins nor byte-compatible. Collect your
              prize on the way out.
       
            EdwardDiego wrote 3 hours 13 min ago:
            Thank you, I'll read your link, but really appreciate your
            perspective. :)
       
          Guvante wrote 5 hours 31 min ago:
          Rust leaves it unrestricted. As does Haskell (although I am lying a
          little most just choose to use Either for Result)
          
          Rust actually gets some use out of it, for instance returning an
          alternative value instead of handling it as a pure failure.
       
            EdwardDiego wrote 5 hours 23 min ago:
            Oh true, I am really interested in those choices too.
       
              remexre wrote 5 hours 4 min ago:
              The case that comes most to mind for that in Rust is
              
   URI        [1]: https://doc.rust-lang.org/std/primitive.slice.html#metho...
       
                EdwardDiego wrote 3 hours 12 min ago:
                Thank you!
       
        omcnoe wrote 6 hours 15 min ago:
        As a daily user of F# I'm not sold on this suggested alternative.
        It seems to be trying to make it as easy as possible to make the Result
        type behave like exception handling, but I think there are some key
        downsides to the approach.
        
        First, Result is often used in place of Choice only to communicate the
        intent that the 2nd case is an error case. Making it more work to use
        Result (by requiring the types used for the Error to implement some
        special interface) means programmers will simply use Choice instead.
        
        But the bigger issue is that this suggested change would remove the
        ability to ensure that cases of an error type are exhaustively matched.
        They introduce the FailAs active pattern at the very end of the blog to
        deal with logic that wants to inspect the underlying error cases, but
        active patterns break the compilers ability to check if a match is
        exhaustive. This is a really useful property for program correctness, I
        can ensure that a caller of my function will receive a compiler error
        when a new error case is introduced that wasn't previously handled.
        
        I wonder if a better design for the error type could be some kind of
        error type set that could automatically collect possible error cases up
        the call stack, so that when confronted with an IFault you at least
        know what possible cases it could have. With some type inference help
        it might not even be that painful to deal with, the first example could
        automatically infer a type like Errors.
        
        As touched on briefly in the article, the elephant in the room here is
        that changing the Result type doesn't do anything to unify the two
        distinct worlds of exceptions vs error values that currently exist in
        F#. Dealing with obscure C# exception flow from F# has been the biggest
        source of subtle, hard to track down bugs in my production code.
       
          needlesslygrim wrote 5 hours 28 min ago:
          I agree, this doesn't seem to be much better than adding `throws
          Throwable` to all your methods that can fail in Java. The reason sum
          types are so useful for error handling is that all possible cases can
          be exhaustively checked against at compile time.
       
       
   DIR <- back to front page