Hacker News new | ask | show | jobs
by baq 1653 days ago
I mostly agree, but there's one place where it does make a lot of sense to keep the hierarchy open: exceptions. Ability raise a specific error and catch it in a generic handler is very useful.
2 comments

Interfaces would work fine here as well.
As someone who is greatly in favor of composition over inheritance, I don't agree - or at least my experiences don't point that way.

Both Rust and Go have had medium sized warts and/or boilerplate around errors, especially if you need control flow to depend on the error. In Python I've never felt that way.

Not sure I can put my finger on it, because any trivial example would be fine in either paradigm. I think it has to do with the forced/unnatural upfront decision "is this error a type or an interface" that may change later on, as a type might need to be refactored to an interface. There's probably one or two other reasons I can't think of right now.

Standard ML and OCaml have both sum types (like in Rust) and exceptions. I feel like both have their value, and both are important. A nice thing is that you can match on exceptions:

    match number_of_lines_of_a_file() with
    | x -> x
    [ exception File_not_found -> 0
This makes it really easy to create alternative versions of functions, using exceptions or option types:

    match number_of_lines_of_a_file_opt() with
    | Some x -> x
    | None -> raise File_not_found

    match number_of_lines_of_a_file_exc() with
    | x -> Some x
    | exception File_not_found -> None
flow control is always impacted by errors. rust and go just don't hide that on you with a giant 'go to random location in the stack' capability.
Agreed. Exceptions tend to trivially respect the Liskov substitution principle and generally hold little state besides a debugging string. It's when you subclass an object as a method of code reuse that you start running into problems.