Haskell for Imperative Programmers #27 - Exceptions

แชร์
ฝัง

ความคิดเห็น • 6

  • @rodrigo_botti
    @rodrigo_botti 3 ปีที่แล้ว +9

    After reading a little bit on the internet, I managed to make the `((\var :: Type) -> block)` syntax work by adding
    {-# LANGUAGE ScopedTypeVariables #-}
    to the first line of the file

  • @JinnyjinnyJin
    @JinnyjinnyJin ปีที่แล้ว +1

    Use purely functional concepts (Maybe and Either) as much as possible, and use exceptions only when it's unavoidable. Thanks!

  • @mouduge
    @mouduge 21 วันที่ผ่านมา

    Awesome tutorial! However, I'm really not sold on Haskell's error management, it feels like a bad compromise between "errors as data" and "errors as special control flow". I think exceptions are a really bad idea, they go against the spirit of functional programming, they're easy to mishandle, they complexify many situations, they kill kittens, and the world would be better off without them. Your justification for them at 9:55 is that "if the whole runtime is failing, then only exceptions get through". But why? Why couldn't errors be returned as data? Rust manages it, it's definitely possible.
    IMHO, the main downsides of "errors as data" are:
    1) If function f1 calls function f2 which calls function f3, and so on up to function f100, which returns an error that needs to bubble back up to f1, then all the intermediate functions f2 to f99 have a bit of work to do, which exceptions can avoid. But some synctactic sugar can make this as easy as a single character, for example Rust has the ? operator: when f99 calls f100(...)?, if the return value of f100(...) is an error, then f99 immediately returns this error. There's also map_err in case you need to tweak the error before returning it. This makes the pass-through very lightweight, and I actually like the fact that it's explicit. Exceptions can go through unnoticed, which leads to programming errors.
    2) If a function h calls multiple functions g1, g2, g3, ..., g100, which may return errors E1, E2, ..., E100 respectively, and if we suppose that function h just wants to let these errors bubble up to the caller, then its error return type will be E1 | E2 | E3 | ... | E100. In Haskell you will have to define a custom data type just for function h, for example `data ErrorH = ErrorH1 E1 | ErrorH2 E2 | ... | ErrorH100 E100`. If another function G may return errrors E2 | E3 | ... | E101, then it will need its own type ErrorG, which will be treated as completely different from ErrorH by the compiler. Sadly, Haskell does not seem to support such "structural sum types" (e.g., as opposed to languages like Typescript, Flow, Elm, ROC, or Julia, which let you use E1 | E2 | E3 as a perfectly valid type). Please correct me if Haskell does have structural sum types, that would be great! Without structural sum types, you have to deal with every possible combination of errors with its own new type. I can see why exceptions are tempting in this context. Note that Rust doesn't support structural sum types either, and therefore you have to use hacky external libraries to work around these difficulties. And this limitation encourages people to bundle all related errors into a single type, like IOError, you lose a lot of granularity. In short, I don't like Rust's error system either.
    I haven't played with ROC yet, but its error management looks fabulous. There are no exceptions, it's just errors-as-data, with nice synctactic sugar to make passthrough pretty lightweight (+ explicit + flexible), and with structural sum types that are automatically inferred by the compiler, and let you be as granular as you want with your errors, without any typing nightmare, and proper exhaustivity checks to ensure every error is dealt with. Check out this great talk for more details: th-cam.com/video/7SidSvJcPd0/w-d-xo.html
    Perhaps Haskell could evolve in that direction too?

  • @shutterrecoil
    @shutterrecoil 2 ปีที่แล้ว +1

    What happen with exception from first action if finally second action also throws an exception or throws an exception and catches it ?

    • @mouduge
      @mouduge 22 วันที่ผ่านมา

      I just ran some tests with `finally f1 f2`:
      * if f1 throws an exception e1, and f2 throws an exception e2, then `finally` throws exception e2. Exception e1 is lost. 😞
      * if f1 throws an exception e1, and f2 is successful (including if it throws an exception e2 but catches it), then `finally` throws exception e1 (after running f2 of course).
      * if f1 does not throw an exception, but f2 throws exception e2, then `finally` throws e2. The output from f1 is lost.
      * if neither f1 nor f2 throw an exception, then `finally` returns the output from `f1`. The output from `f2` (if any) is always dropped.

  • @BashkaMen
    @BashkaMen ปีที่แล้ว

    what about 'head' function without IO ? head []