Errors as Values are the Future

Поделиться
HTML-код
  • Опубликовано: 7 сен 2024
  • НаукаНаука

Комментарии • 367

  • @AGeekTragedy
    @AGeekTragedy Месяц назад +185

    "throw is just a fancy goto". I mean yes, but all control flow mechanisms are just fancy goto.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +36

      True, but most are predictable. Throw could take you anywhere

    • @user-uf4lf2bp8t
      @user-uf4lf2bp8t Месяц назад +2

      Gleam doesn't have those, other than panic. No if else, no loops, no early returns, no exceptions.

    • @AGeekTragedy
      @AGeekTragedy Месяц назад +12

      ​@@user-uf4lf2bp8t I'm pretty sure pattern matches and function calls both have gotos in them if you strip off enough layers of abstraction
      To be clear, i think the abstractions are good and was mostly just being silly saying everything is a goto.

    • @oracleoftroy
      @oracleoftroy Месяц назад +10

      ​@@IsaacHarrisHolt Throw could take you anywhere in the same way return could take you anywhere. In reality, throw works like a super return, it is only going to take you up the call stack, not to some random place in the code that has a try/catch. Saying it can take you 'anywhere' always makes me assume the person has never actually worked with exceptions.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +12

      Yes, it can only take you up the call stack, but unless you have visibility on the whole call stack, you can't see where that error is going to pop out.
      Also, you don't KNOW which functions are going to throw, which is the whole problem.

  • @laughingvampire7555
    @laughingvampire7555 Месяц назад +178

    Also, people forget that errors as values is something that it was used in C originally and people invented the try/catch because people got tired of handling errors as values.
    so is just the pendulum is swinging back to the other side.

    • @tiranito2834
      @tiranito2834 Месяц назад +27

      Exactly. People just easily forget the past. We once were very happy with simple errors as values returned from functions, and this comes from even before C, it was just the simplest, most natural way of doing things considering how computers actually operate with data under the hood... yet for some reason someone decided that we needed exceptions. Now, we're coming back to errors as values, but, yet again, just like with exceptions, there seems to be a need to overcomplicate the system when you can just return a simple integer value for the error... the pendulum is indeed swinging back, but with each swing, it appears that the artificial complexity obtained in one swing is carried over to the next. Let's see when we finally come back to simple integer return types for errors and no structs with Ok and Err return types or anything like that.

    • @mysterry2000
      @mysterry2000 Месяц назад +50

      ​​@@tiranito2834I personally think it's good we're not using integers anymore since enums makes the code more readable and less hacky.
      With integers you can introduce infinitely many variants without a way to track them without a convention in place, whereas enums enforce keeping all variants within a single declaration, so that's a win for readability.

    • @mattmurphy7030
      @mattmurphy7030 Месяц назад +22

      @@mysterry2000wait til you find out that integers and enums are interchangeable in C

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +65

      I'm on the enum side of this argument. Even if they're functionally the same thing, having a named, pre-defined set of values your function can return is infinitely better than just returning "some number", and not every C program follows the same standards. Argon2, for example, returns -1 through -36 ish for their errors, whereas other libs may use positive ints.

    • @nubunto
      @nubunto Месяц назад +23

      Yeah enums today and strong typing offer a much more ergonomic experience than magical error codes in C. Even if they are interchangeable

  • @Qrzychu92
    @Qrzychu92 Месяц назад +38

    The only thing that exceptions have over errors are values is the fact they always come with a call stack. When something goes wrong, you know exactly which file to open and in which line to look, or at least where to put a breakpoint.
    I would say until your lang supports adding stacktrace to the errors, it becomes much harder to determine the reason for an error

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +6

      You can use something like Snag to add context to your errors in Gleam, or Go has the ability to wrap errors. That provides enough context as long as you're diligent, I think. But yes, they don't come by default.

    • @user-uf4lf2bp8t
      @user-uf4lf2bp8t Месяц назад

      This is true for older languages like ocaml and haskell where the norm is just to use options, but result types with an error type and an ok type are better because the error variants come with info. These are more popular in rust and gleam.

    • @samuraijosh1595
      @samuraijosh1595 Месяц назад

      ​@@user-uf4lf2bp8tlol Haskell already uses the original form of result type, it's called Either.
      data Either err value = Left err | Right value

    • @kered13
      @kered13 Месяц назад +3

      @@IsaacHarrisHolt The problem is that programmers are not diligent.

    • @Rudxain
      @Rudxain Месяц назад

      An ("ugly") solution are what I call "meta-enums". The deepest function in the stack would return a typical `Result` where `E` is a custom enum. Then any fn that calls that fn must return a `Result` (defined as a custom `Result` enum, for readability) where `F` is `enum {self_err(F), inner_err(E)}`

  • @rodrigohmoraes
    @rodrigohmoraes Месяц назад +169

    Tom truly is a genius. We're all quiche-eaters when compared to him

  • @kered13
    @kered13 Месяц назад +4

    I have worked extensively with systems that both use exceptions, and those which exclusively use error values. I have found that any sufficiently complicated system using error values eventually reinvents a less useful version of unchecked exceptions. Less useful because they typically do not provide a call stack, making debugging more difficult. The resulting code is also inherently more noisy, although mechanisms like Rust's question mark help to minimize this drawback.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I'm curious how you reinvent unchecked exceptions using error values. An unchecked exception is one you don't know can be thrown, but error values appear in the function signature.
      And I think too many people put emphasis on keeping code concise. I'd rather have to write a load of error handling code than not know where something is going to go wrong.
      And as for stack traces, this is fair, but you can provide context within an error.

    • @kered13
      @kered13 Месяц назад

      ​@@IsaacHarrisHolt The problem is that a sufficiently complicated system will depend on many different libraries, each of which returns a Result with it's own error type. You can union these all together, but it becomes very messy. The next step is to map the error onto your own error type, but in doing so you lose some information from the error type, and you add even more noise to your code. The final step is that you either adopt or invent some generic error type that can wrap all other errors, now you have a unified error type for your entire application, but it's effectively just an unchecked exception, as the error type does not actually tell you any useful information.
      Snag is an example of this. If almost every function in your code returns snag.Result, then you aren't getting any information from the type. It's just an unchecked exception. The context is nice, but that's still basically just the stack trace of an exception.
      And almost every function will need to return a type like this, because in a sufficiently complex project almost every function can fail. The only functions that can't fail are those functions that are so trivial that you don't even need to look at them to know that they can't fail.
      I spent several years working on C++ code like this, because exceptions were forbidden in that codebase. And it was fine, our Result library was very good, we had macros that acted like Gleam's `use`, and we even implemented our own context like in Snag (this was long before Gleam or Snag existed, just convergent evolution). However after all that experience I never felt that it offered us anything that exceptions did not.

    • @MadsterV
      @MadsterV Месяц назад +1

      ditto. Also, panics.
      Forcing the caller to handle the error often results in passing the error up the stack as a return value because that wasn't the appropriate level to handle it.
      Exceptions are really good at what they do.

    • @kered13
      @kered13 Месяц назад +1

      @@IsaacHarrisHolt The problem is that any sufficiently complicated system will depend on multiple libraries, each of which returns it's own error type. When you have a function that calls two different libraries, each of which can fail with different error types, you have to union those error types together. This quickly becomes unwieldy. So you use map error to map the library errors to your own error type, but in doing so you have lost the type information of the underlying error, and you have added more noise to your code. The next step is to use a library that wraps all error for you, but this is no more useful than unchecked exceptions. In a sufficiently complicated system, almost every function can fail. The only functions that can't are so trivial that you can tell just from the name that they don't fail. So you don't get much useful information from a function that returns a generic error type.
      Snag is an example of such a library. When every function returns `snag.Result(t)`, what information is this really telling you? The Snag contexts are nice, but they are basically just the same stack trace you would get from exceptions, except they can be skipped and produce more noise in the code,
      I spent a few years writing C++ code that exclusively used error values like this. It was fine, we had our own Result type, we had macros very similar to Gleam's `use` that made it easier to use, and we even added our own context support much like Snag (this was all years before Gleam or Snag existed, this is just convergent evolution). However after all this experience I never felt that the system offered any advantage over unchecked exceptions. When I write code using unchecked exceptions, I just assume that every non-trivial function call can fail, and this has never caused issues for me.

  • @georglauterbach8972
    @georglauterbach8972 Месяц назад +30

    „This pattern is very similar to the way the question mark operator works in Rust. But in Gleam, it’s just a function - no special syntax required.
    However, call-back based code like this can become quite nested. In the world of JS, this is often referred to as „callback hell“. […] Luckily for us, Gleam has a special syntax for flattening out callbacks.“
    Do you want the special syntax now, or don’t you?

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +8

      Fair point! The `use` syntax in Gleam is used for more than just error handling though, and it's completely optional. But yeah, I see where you're coming from :)

    • @georglauterbach8972
      @georglauterbach8972 Месяц назад +4

      I thought it was funny that the video does not seem to recommend special syntax first, but then proceeds to use it. Either way, I like both.
      To be fair, the `?` Is optional as well and just syntactic sugar for a match expression.
      I‘m quite curious how Gleam compares to Rust. Seeing that it is built on top of Rust (nice), I would expect to be able to do all that Gleams aims to do with Rust as well; at least this is my initial impression. Maybe a video about Gleam vs Rust would be interesting too :)

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +4

      Gleam is built using Rust, but they're fundamentally different languages. You're not the first person to compare the two though, so it's probably worth making some sort of video to clear that up 😅

    • @MadsterV
      @MadsterV Месяц назад +1

      and this syntax is 'async'
      sorry I meant '?'

    • @Turalcar
      @Turalcar 27 дней назад

      @@IsaacHarrisHolt Can you use it with something like foreach? If so, it feels like a blatant violation of the least surprise principle, that after a certain point the code can be executed multiple (or 0 but early return is less shocking) times.

  • @ChristOfSteel
    @ChristOfSteel Месяц назад +31

    Isn't this just plain monadic computation, that's being used in functional programming since... forever?

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +26

      Yep, but the rest of the industry has finally realised it's a good thing

    • @Axman6
      @Axman6 Месяц назад +11

      But they will still all complain when you use the word monad and claim it’s too difficult to understand.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +6

      100%

    • @user-uf4lf2bp8t
      @user-uf4lf2bp8t Месяц назад +4

      Errors as values isn't always monadic. In gleam it is, but go just returns 2 values and you check if the error value is not null.

    • @Axman6
      @Axman6 Месяц назад +11

      @@user-uf4lf2bp8t this is arguably still monadic, it’s just that you have to write the machinery again and again and again. I’m always shocked when I read Go code and every second statement is if err != nil { … }. Programming is about abstraction but for some reason the developers of the language seem intent on discouraging abstraction as much as possible. It’s always felt like a language designed for lines-of-code-written metrics, and the constant explicit error checking obscures the business logic.
      What’s particularly frustrating about Go is that it uses errors as values, in a way that is easy to get wrong and correct use is only enforced by convention. Compare Haskell’s Either, which is either an error or a value, never both, and you must pattern match on it before you can access the value. Go’s multiple return type and the “return some hopefully innocuous value along side the error because we don’t have sum types” approach is nuts to me.

  • @Karurosagu
    @Karurosagu Месяц назад +6

    When you use errors as values you also get the advantage that the LSP of the language that you are using can help you
    Your LSP can't help you If the funcion that might throw an error has a try-catch inside: You have to leave your editor and go to the documentation for that function or (worse) take a look at the entire code + the code for the exception and so on, and all of that process is annoying, it breaks your flow

  • @Jojor11
    @Jojor11 Месяц назад +41

    errors as values is so good once you use them that now every exception is painful to use/handle.
    literally thought of making a custom result type on work projects, but since i'd have to handle exceptions anyway it was not worth it...
    but yeah, the best thing is knowing what can fail and how instead of just fill everything with try-catch just in case its possible for a random unknown exception to appear.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      We use a custom result type in our TS code at work, and it's sometimes a bit much, especially when there are other alternatives that better leverage the TS type system.
      But yeah, it's nice to know what's gonna break your code and be able to prepare for that.

  • @LethalChicken77
    @LethalChicken77 Месяц назад +2

    My code never breaks. It just has happy little features.

  • @loganhodgsn
    @loganhodgsn Месяц назад +4

    It depends on the error. If you bubble an error up the stack, more and more tracing data will have to be stored and passed around.
    On the other hand, panics allow you to perform an autopsy on the stack without incurring any runtime penalties (because all panics are assumed to never occur, because if they do, it's lights-out for the program anyways).

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +2

      Yes, I agree, but panics and exceptions aren't the same, which I think is something a lot of people seem to miss. And adding that context is what Snag is useful for

  • @Axman6
    @Axman6 Месяц назад +18

    WOW, IT’S ALMOST LIKE HASKELL HAD THIS RIGHT THIRTY YEARS AGO. *cough* sorry, had something stuck in my throat.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +5

      Never said it didn't! But the rest of the industry is finally catching up

    • @Daniel_Zhu_a6f
      @Daniel_Zhu_a6f Месяц назад

      every normal low-level language has type unions, calm down.
      exceptions are much better than values when you work in a REPL though, that's why even functional scripting languages use exceptions. as somebody who works with python (for data processing) i can tell that the trick about working with exceptions is not to catch them, unless you really need to, and not to throw them, unless the error is so big that it should reset execution to the most recent checkpoint (eg drop a record from a table, cancel/redo a task). then exceptions become quite manageable.
      but in standalone programs exceptions get out of hand very quickly indeed

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I'm curious, what do you do in the case where you have a small error? Do you return it as a value? In which case, you're doing it the Gleam way already! You're effectively using exceptions to crash and reset your process, which is offensive programming and would be managed in Gleam using a supervision tree.

    • @Daniel_Zhu_a6f
      @Daniel_Zhu_a6f Месяц назад

      ​@@IsaacHarrisHolt depends on a case.
      may just log it (eg. when iterative algorithm does not converge, but there is still some sort of result). i also just log any unexpected exceptions when i batch-process a lot of files, so that a script won't stop because of one bad file.
      when error is very much expected (parsing, validating) may want to return a type union ( i usually use pair of int/enum + value, to indicate what value means, kind of like one would normally do type unions in C)
      then there are less common options:
      collect information about errors (input values, error messages) into array and return it / store in a global variable. this is just like logging, but you can easily use this data further down the line, eg select only those table records that give rise to a certain type of error. of course, this can be done with well-structured logging system, but list approach is much simpler.
      it is possible to feed errors to an impure error-handling function that does something complex. eg if you may want to have email/telegram notifications + save broken data separately, but this is more of a standalone program territory, than a repl session/script. however, a handler function may be useful in other scenarios in a repl context: i often use handler functions for plot post-processing (appying certain common adjustments, saving, deallocating) -- this way, i don't need to write same piece of code over and over again, but at the same time have several radically different behaviors, that i can switch on the fly by reassigning a global variable to a different function.

    • @Daniel_Zhu_a6f
      @Daniel_Zhu_a6f Месяц назад

      @@IsaacHarrisHolt depends on a case.
      may just log it (eg. when iterative algorithm does not converge, but there is still some sort of result). i also just log any unexpected exceptions when i batch-process a lot of files, so that a script won't stop because of one bad file.
      when error is very much expected (parsing, validating) may want to return a type union ( i usually use pair of int/enum + value, to indicate what value means, kind of like one would normally do type unions in C)
      then there are less common options:
      collect information about errors (input values, error messages) into array and return it / store in a global variable. this is just like logging, but you can easily use this data further down the line, eg select only those table records that give rise to a certain type of error. of course, this can be done with well-structured logging system, but the list approach is much simpler.
      it is possible to feed errors to an impure error-handling function that does something complex. eg if you may want to have email/telegram notifications + save broken data separately; this is more of a standalone program territory, than a REPL session/script. however, a handler function may be useful in other scenarios in a repl context: i often use handler functions for image/plot post-processing (applying certain common adjustments, saving, deallocating) -- this way, i don't need to write same piece of code over and over again, but at the same time can have several very different post-processors, that i can switch on the fly by reassigning a global variable to a different function.

  • @floppa9415
    @floppa9415 Месяц назад +1

    I firmly hold the opinion that the only cure for the problem is extensive testing that is as close to a production system as possible and logging as much information to reproduce issues if the come up. From my experience 99% Errors (be that errors as values or as exceptions) one of these 2 things is done when an error occurs: Either bubble it up, or do some implicit stuff that is specified exactly nowhere and probably not consistent to try and save the situation even if means just ignoring the error and continuing on as if nothing happened.
    From my experience the last option has lead to the most catastrophic outcomes and hardest to find bugs. Because instead of having the thing crash right when things go wrong they rear their ugly head on the other side of the planet where the wrong data has been mangled through and the issue might not be obvious.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      This is fair! I definitely agree that thorough testing solves a lot of issues. And having reasonable processes, too.

  • @MrDarkoiV
    @MrDarkoiV Месяц назад +2

    Honestly. I like exceptions.
    My only issue with exceptions is they are implicit, where they should be explicit.
    Any function that can throw should be marked that it can throw.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      That's basically just errors as values then, especially if you're then forced to handle the exception

    • @MrDarkoiV
      @MrDarkoiV Месяц назад +5

      @@IsaacHarrisHolt Yes, except you have clear separation of concerns, and you don't have to pollute codebase with sum type results when they should not be handled locally.

    • @MadsterV
      @MadsterV Месяц назад

      I take it as everything can throw, which is usually the case

  • @maruseron
    @maruseron Месяц назад +1

    The problem isn't exceptions - it's that you can treat possibly-throwing code as safe code without any repercusions. Java did well with checked exceptions, but everyone hates them because they're annoying to deal with.
    What you need is a language that forces you to handle failures immediately in a convenient way, values or not.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I would say that Gleam is that - you can't access the result of the computation without at least acknowledging that there's an error

    • @maruseron
      @maruseron Месяц назад

      @@IsaacHarrisHolt oh, yes. I do like Gleam's approach to error handling. I think errors as values is generally more solid than exception-throwing code.
      My point is that exceptions aren't as obsolete as it's somewhat painted in this video, but rather them being unwieldly is a result of little design effort put into them.
      For instance, there's a design article from one of Java's lead architects (inside.java/2023/12/15/switch-case-effect/) that proposes an addition to the switch's pattern matching capabilities to catch errors in a functional vein - similar to how effect handlers work. And this is no coincidence: FP's effects and Java's checked exceptions are equivalent constructs in these paradigms (FP vs imperative). The sudden ease in failure handling incentivizes Java users to add checked exceptions to their function signatures more widely, achieving the same success in terms of safety without errors as values.

  • @toxicitysocks
    @toxicitysocks Месяц назад +64

    As a gopher, I never want to use a language that doesn’t have errors as values

  • @hanro50
    @hanro50 Месяц назад +4

    Personally, if I wanted to get errors as values....I'd use C. However, in my opinion, handling it that way gets to be rather annoying rather quickly.
    An exception in a language that supports a try catch like system can halt a borked function if it is raised. If you're just returning a value, you risk someone forgetting to add a check to see if the returned result is actually valid.
    Maybe you have a function that should return an offset for another part of your code, but for whatever reason, it encounters an error and returns -1. One simple oversight later, and now you've managed to not only crash prod, but you've corrupted several customer records in the process.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      That's true when your error code is just an int, but in Gleam you can't access the value in the Ok without at least acknowledging that there could be an error

    • @MadsterV
      @MadsterV Месяц назад +1

      @@IsaacHarrisHolt same in exceptions.... and if you can't handle at the caller level, you let it pass so the next level up can handle it.

  • @michaelangelovideos
    @michaelangelovideos Месяц назад +27

    Coming from JS world, errors as values is such a good feature🙌

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +3

      Agreed! Exceptions are painful

    • @IvanKleshnin
      @IvanKleshnin Месяц назад +2

      You can return Error objects instead of throwing them... It will give you roughly the same benefits in TypeScript. Just wrap 3rd party functions that throw and never throw yourself. Not as slick as in Gleam but much better than try/catch madness with half of code flows being untyped.

    • @MadsterV
      @MadsterV Месяц назад

      ​@@IvanKleshnin you can even check the error type and conditionally rethrow or handle it. Exceptions are excellent, people just don't spend effort understanding them.

    • @IvanKleshnin
      @IvanKleshnin Месяц назад

      @@MadsterV that's funny. Good luck with programming.

  • @foo0815
    @foo0815 Месяц назад +4

    It's a mistake to mix error handling with returning, no matter if returning Error values or returning multiple stack levels by an exception, because returning destroys the context in which an error might have been correctable. Have a look how the Common Lisp condition system works for a better approach.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I'm not familiar with it, but I think that a good error will contain the context required to debug it easily. I don't completely agree with your take here

  • @cooperhill6054
    @cooperhill6054 Месяц назад +1

    Always argued with colleagues about their philosophy using try/catch and throwing random exceptions everywhere, instead of just returning an error-type, using a reference to "catch" the error in, or always returning a boolean to signal success of a method. All errors are expected and throwing UnexpectedExceptions is just a sign of a bad programmer. Fight me.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I don't want to fight you! But I'm not sure it's necessarily the sign of a bad programmer. A lot of it is down to the available tooling. Some languages don't have support for multiple return values, others don't have good pattern matching, etc.
      Errors as values work best when you have compilers and linters that can point out the fact you've missed an error, or where your FORCED to acknowledge the error value

    • @MadsterV
      @MadsterV Месяц назад

      Exactly. Throwing generic exceptions with no info is the same as a webservice returning 500s with no body. Useless and bad code.
      Imagine your returned error is always -1 and you're at the same spot.

  • @laughingvampire7555
    @laughingvampire7555 Месяц назад +6

    the thing is that exceptions has always been the wrong mechanism. If you use condition-restart system of common lisp you get all the benefits of errors as values as well as the benefits of try-catch and some unique to conditon-restart system like unwind the stack, identify the mistake, fix the bug in the execution environment and then rerun the stack and finish the original process. You can also store the entire stack to do it later or send it to another environment.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I'm not familiar with any LISP flavours at all, but that sounds interesting! I'll have to give it a look

  • @Greenmarty
    @Greenmarty Месяц назад +4

    I heard Crowdstrike has been experimenting with offensive programing lately ...

  • @edmundas919
    @edmundas919 Месяц назад +7

    I disagree from my (C++) perspective for at least 3 reasons:
    1. If functions calls another functions and in the deep end you get an error, to propagate the error back now you need to check for error at each level.
    2. If your function calls multiple functions you'll need to check for error after each call. This becomes very error prone when you need to release resources manually.
    3. If you pass callback to 3rd party library (e.g. standard library) and callback returns error, there is no way for it to handle error to propagate it back.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +3

      I don't think that "having to" handle errors is a bad thing. It forces programmers to understand the error surface of their APIs and will ultimately promote a better understanding of their program as a whole.
      Also, I don't see how checking for errors is error prone? You're literally forced to deal with them.
      3 can be an issue, I agree, but this is mostly solved by generics and good API design.

    •  Месяц назад

      @@IsaacHarrisHolt imagine you check the current 3 types of errors that some call can throw. Tomorrow the API adds a fourth error and your code is not updated (because reasons).

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      If you're using a language with errors as values and exhaustive pattern matching, you can't get into this situation in the first place, because your code won't compile!

    • @MoguMasso
      @MoguMasso Месяц назад +1

      ​@@IsaacHarrisHolt If you're code is linked dynamically, which is most of the time, it can happen.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      You don't link code dynamically in Gleam :)

  • @Mnementh-ub8md
    @Mnementh-ub8md Месяц назад +3

    I have to say, I think this whole discussion is borked. The video claims exceptions bad with stuff that is solved in some exception languages. Sure, if you compare with Javascript it will be bad. But other languages do exist, and they do stuff better. Similarly I could claim that errors as value are bad, because it is shit in C (the whole reason exceptions were invented is how bad it is).
    But I do not. Because I use both exceptions and errors as value. And I do that even in one code base. For instance in Java or Kotlin I can (and I did) build types that can contain an error. But I can also use exceptions beside them. The only problem here is, that the function decides in which way it relays errors. But in a error-as-values language I have difficulties to emulate exception when they are the better solution (and they can be). At least since Go the defer keyword exists, which solves at least one problem, as it brings the finally-block of exception-languages into error-as-value languages. Does Gleam support defer?
    Anyways, I wish for language support in which the caller could decide what way to use. It wouldn't even that difficult to begin with. The function declaration has to contain an indication if it can error. Then the caller can either call it in a way, say like this:
    val optional_result = ?example();
    This syntax could automatically wrap the result into an union or option (or whatever the language has): either an error or the correct result. You could unwrap after checking for the error-state.
    Alternatively we could use an exception syntax:
    val result = !example();
    In this case result already contains the unwrapped result but in the case of an error it goes to the next catch or if not present the function with this call has to declare to be able to error itself to bubble it up.
    That doesn't seem too complicated, why does nobody do it? Are people too much wrapped up in their thinking that one way of error handling excludes the other?

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Firstly, sure, Java and Kotlin have something, but while you're forced to show that your function throws, are you forced to handle it? If you are, that's basically errors as values 😅
      Gleam supports defer via `use`, if you write a defer function.
      The reason language creators don't implement both is because it would add complexity to programs, and errors as values are generally preferred these days. Sure, C got it wrong, but that doesn't mean the whole concept is bad.
      That said, the whole concept of exceptions? Pretty bad 😁

    • @Mnementh-ub8md
      @Mnementh-ub8md Месяц назад +2

      @@IsaacHarrisHolt Yes, if a function throws, you have to either handle it or bubble it up (by declaring the current function also throws). Well, what you say about C is my argument, only because JS has a terrible implementation that doesn't mean Exceptions are bad. And if you say that Java and Kotlin are basically like error-as-value you see that these concepts aren't as antithetic as you paint it. But you get back to claiming Exceptions are bad, based on nothing.
      And as you say, modern error-as-value has become a lot better since it adopts more and more ideas of exception handling and both get closer. So implementing both interchangeable as I described shouldn't be that difficult.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Java and Kotlin makes two languages, and it's still not a great approach. Java has unchecked exceptions like JS, which arguably makes things worse, as checked exceptions lull you into a false sense of security thinking you've handled everything when you might not have done.

  • @OmegaMusicYT
    @OmegaMusicYT Месяц назад +19

    I do not agree with the premise of this video.
    You know what else is just another word for goto? Return.
    Each exception doesnt create a new control flow, there is a flow for correct values (return) and a flow for errors (raise). All correct values are returned to the function call and all errors are raised to the try/except block. There are no other flows besides those two.
    Also, the "you no longer have to litter your code with try/except blocks" claim is technically true, just that you litter it with if statements and match cases instead.
    Finally, with exceptions I can simply write the only correct path for my code, and if any of the calls I make fails, If its well coded it will rise an exception and I can catch that from the caller.
    With errors as return values I must check each return value for errors at any step, whereas try/except allows me to handle all possible errors through the caller with a single try/catch statement.
    Try/catch statements are great because they can cover an entire function call, and I don't care where the function breaks, I can simply code the correct path and handle any errors later. Its a much more effective approach and its only confusing when people use error values in return in languages with try/except statements or when people dont declare which exceptions their code throws for each case, and return values as errors solve neither of those problems.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +4

      Writing try/except over your entire function is a terrible way to write code. You won't know which part of your function is breaking, meaning debugging will be ten times harder.
      Also, you don't know which functions are gonna throw, so you have to wrap EVERY function call with try/except unless you have full access to the source and the source of every function it calls. That's what I meant by the littering comment.
      At least with errors as values you only need to add extra handling when you KNOW there's going to be an error.

    • @TurnerXei
      @TurnerXei Месяц назад +9

      @@IsaacHarrisHolt In any language with stack traces or exception values you definitely know exactly where an error occurs. I've never seen anyone wrap every function in an exception handler, but I have seen nearly every function have a conditional that checks for error values. Any half-decent API doc generator should tell you if the a function throws exceptions, you don't need access to the source code.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      Most API doc generators can't tell you if it throws because most languages don't have anything to mark that. The doc generator would have to crawl your source code and the source of every function you're calling.
      Also, yes, stack traces give you the location, but only AFTER the error has occurred. So you know what WENT wrong. Errors as values tell you every single thing that COULD go wrong.

    • @Mnementh-ub8md
      @Mnementh-ub8md Месяц назад +1

      ​@@IsaacHarrisHolt You won't write an try-catch over your function, if you intend to just keep it as error-condition. You just throw it up.
      Also you know which function throw an exception, because exceptions are part of the functions signature in most languages with exception-handling and the compiler will enforce that you handle the exception - but only if one is thrown in the code you call.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      There are relatively few major languages where exceptions are part of the function signature. I know Java has the option of doing it, as does Swift, but iirc it's not always enforced. And in languages where you don't have it, your tooling would need to recursively check every function call down to the native layer to see all the kinds of exceptions that can be thrown.

  • @MadsterV
    @MadsterV Месяц назад

    All of this goes away if you learn to use exceptions properly :)
    Callback hell was solved in most languages years ago with the introduction of async (what a godsend, no more hadoukens!).
    I've been using exceptions for years in the (*gasp*) weakly typed world of JS without issues. No random crashing, I know exactly what happened and where and I can recover from it if it makes sense to do so.
    I always laugh out loud when I see a new exception-free language and then they explain panics.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      It's not a case of "learning to use exceptions properly".
      Fundamentally, these are two different solutions to the same problem. The difference is that one gives you upfront visibility over what COULD go wrong, so you can deal with it while you're writing the code, and the other tells you what DID go wrong when your code has already crashed.
      Panics are meant for unrecoverable situations only. Exceptions can be and are used in both recoverable Andy recoverable situations, making it difficult to discern and differentiate.

  • @dadlord689
    @dadlord689 Месяц назад +2

    Like there were not enough null ptr problems, so founders have developed the concept of error so generation to come have proper suffering routine even when coding becomes easier.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I'm not sure what you're saying here

    • @diamondkingdiamond6289
      @diamondkingdiamond6289 Месяц назад

      @@IsaacHarrisHolthe’s saying you’ve created a made up problem that doesn’t actually exist😂

  • @seasong7655
    @seasong7655 Месяц назад +5

    How is it less complex than a try catch? You still have to handle the error cases. It has the same amount of logic behind it.
    In Java the functions that throw errors also have to be annotated, so you also see what error the functions throw in the same way as error return types.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +4

      True, but not every language is like Java. And complexity has nothing to do with how many lines of code you need to write, it's about being able to reason through your program.
      If I'm reading a Java function, and I see something that throws but isn't immediately handled, I have to look at all that function's callsites, and all those functions' callsites, etc. all the way up the call stack to figure out where it's going to get dealt with, or even if it's going to get dealt with at all.

    • @samuraijosh1595
      @samuraijosh1595 Месяц назад +3

      Because it's genuinely a nice looking abstraction over explicit nested if else's. I'm not for bullshit language abstractions but this one is just naturally useful as it's not merely syntactic sugar. Under the hood its monads but the syntax makes it useful without you even knowing the theory behind monads.
      In Haskell it looks like this:
      data Maybe a = Nothing | Just a
      getDetails :: Name -> Maybe(Details)
      getDetails name = do
      personId

    • @lengors7327
      @lengors7327 Месяц назад

      ​@@IsaacHarrisHolt How is thay any different from returning a result??????????

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      With a result you HAVE to handle it somehow. You also get better type safety, and useful functions you can use to manipulate them since they're just treated as values.
      Moreover, results are predictable. That's the most important thing.

    • @lengors7327
      @lengors7327 Месяц назад +1

      @@IsaacHarrisHolt If we are comparing it with checked exceptions in java (which is what OC was talking about), then there's little difference.
      1. "You HAVE to handle it" -> you can just propagate it up, you just have to be explicit about it (which yes, it means you are forced to handle it). But that's the exact same thing with (checked) exceptions, you either do something with it or you propagate it up by annotating the functions with the throwing type, otherwise you will have an error (i.e. you are forced to handle it)
      2. "Better typing" -> disagree. With checked exceptions you can annotate the function with the exact type of exception, and you can have multiple ones at that (and you are forced to handle then all). With results you do the same just with different syntax, but I dont see how one is better than the other.
      3. "Useful functions" -> this is completely language dependent (just like exceptions itself, where most languages chose not to have them represented in the type system, while Java, for all of its flaws does - with checked ones at least)
      4. "moreover results are predictable" - in what ways? For functional style programming I can see results being easier to reason about, but for imperative languages what are the advantages? For the caller it's the same thing, for the callee it's not any more unpredictable than returns in the middle of the function.
      Results make more sense, imo, in functional languages, but for imperative languages I dont see any advantages of results over exceptions. The major difference is that with the latter you will usually have a callstack, but not with the former, so which one you use depends on the use case.

  • @IvanKleshnin
    @IvanKleshnin Месяц назад +3

    It's shocking to see so many commenters who don't get it, even after a good explanation. I'll add my 5 cents.
    With exceptions you have your SUCCESS logic paths typed, but ERROR paths remain untyped. Compilers do not enforce exception handling and, in JS especially, there's no typesafety with try/catch whatsoever. So if you're into static types - congratulations, but without errors as values you get at most 1/2 of the whole type safety. And if you're not into static types... my condolescences to your career.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +3

      Finally someone who gets it 😅

    • @samuraijosh1595
      @samuraijosh1595 Месяц назад

      Yes but even as someone who likes and regularly errors as types just blinding wrapping everything in a Maybe/Optionals or Result/Either is not the way to go. Especially when your project is sizeably large even in a language with the syntactic and compiler tools to handle complex errors like Haskell you may corner yourself into some unscalable and unrecoverable mess.
      Use a mix of crashing and type level error handling.

    • @IvanKleshnin
      @IvanKleshnin Месяц назад +1

      @@samuraijosh1595 Haskell is a mess on its own, Gleam does this correctly with a single Result type. It is the way to go.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      Yes, and just to be clear, you CAN crash. What's different is that, instead of resetting to a certain error boundary, you crash your process and let your supervisor start a new one

  • @aarondfrancis
    @aarondfrancis Месяц назад +1

    How'd you get that footage of me on my Lambo?

  • @Jamiered18
    @Jamiered18 Месяц назад +3

    Without going all the way to errors as values, I always found Java's checked exceptions to be really useful, something sorely missing in other languages. I see arguments against them, but I never really agreed and don't see how errors as values don't have exactly the same downsides.
    Furthermore, strict null checking in Typescript has been really useful to me. Null is another value that can slip through unnoticed too easily in many languages. Option types work too in other languages, as long as you are forced to handle them.

    • @electra_
      @electra_ Месяц назад +3

      Pretty much always use TS instead of JS now for this exact reason.
      I also like how TS sort of lets you get away with enum types using a type field.
      That is, you can do something like
      type Result = {type: "ok", value: T} | {type: "err", value: E}
      Now in any code using such a Result, access to value is guarded, but in any if statements that check something like if (r.type == "ok"), it will let you use the corresponding type.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      Both good solutions! I'm not familiar with Java, but we use this style of error in TS at work, and it's pretty great. The problem is one of the functions you're calling in a library might throw, and you'll never know until it does.
      On the null and option side, Gleam has both of those things! Nil is a unique type that can't be assigned to anything else (like () in Rust) and you have the Option type for values that may or may not be present

    • @kered13
      @kered13 Месяц назад +1

      The problem with checked exceptions like in Java is that it doesn't work well with callbacks. This became a significant problem when Java introduced the Stream API. You can't pass any function that can throw a checked exception into stream map, filter, etc. without using some hacky workarounds. This problem can be solved with a sufficiently strong polymorphic type system, like C++ for example, but very few languages have that.

  • @SunshysContentRanch
    @SunshysContentRanch Месяц назад +32

    the more languages we write, the more we find out that C was right all along 😂

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +13

      Yeah but at least we're not stuck trying to decide the values of a million different ints 😂

    • @SunshysContentRanch
      @SunshysContentRanch Месяц назад

      @@IsaacHarrisHolt Absolutely!

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +2

      Gleam probably has some of my favourite error handling all told

    • @SunshysContentRanch
      @SunshysContentRanch Месяц назад +1

      @@IsaacHarrisHolt Gleam is absolutely wonderful. Great Language.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      100%! I love it

  • @PenguinArmy94
    @PenguinArmy94 Месяц назад +1

    Is Gleam good for scripting and test automation? I use Python and love Python but I do miss proper static typing. I could use TypeScript but unsure

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      It's not intended to be a scripting language, but you can run parts of modules if they have a main function. However, like Rust and other similar languages, you do need the project scaffold each time

  • @MegaICS
    @MegaICS Месяц назад +3

    4:33 what's the difference here with try/catch (other than syntactical)?

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +5

      Case statements will force you to handle every variant. With try/catch and exception-style systems, you're never forced to even acknowledge the error exists. In Gleam and other error-as-value languages, you have to face the error down when it arises.

    • @IvanKleshnin
      @IvanKleshnin Месяц назад +3

      Type safety.

    • @kered13
      @kered13 Месяц назад +3

      @@IvanKleshnin Exceptions are as typesafe as the rest of the language. In Java or C++, exceptions are strongly and statically typed.

    • @IvanKleshnin
      @IvanKleshnin Месяц назад

      @@kered13 yes, in languages with typed and checked exceptions. Few of them exist, as you mentioned. But even such exceptions do not have any added value over Result type (throw is basically a goto if not immediately handled).

    • @MadsterV
      @MadsterV Месяц назад

      ​@@IvanKleshnin no. Gotos have a single target location, exceptions crawl back up the stack and they can be handled at ANY point along that crawl.
      JS is not strongly typed, yet exceptions do have types, just not strong (just like the rest of the language).
      also, you're supposed to throw useful information, and they definitely force you to acknowledge the error exists (unless you like topcrashes).
      If you write code that acknowledges the error (a catch) and then do nothing with that information.... well, you could do the same in Gleam and any others like it.

  • @NobodyYouKnow01
    @NobodyYouKnow01 Месяц назад

    What, can other programming languages not quit to desktop?? Also, what happens if someone used this code on top of one that just decided to catch whatever the new language tried and roll through?

  • @MarcoServetto
    @MarcoServetto Месяц назад

    Error handles must be able to support also errors like out of memory or stack overflow.
    Those errors d not work as 'optionals/eithers'.
    Also, what if you get an error while 'writing'' a file instead of 'reading' one? now you get a result Either but.... you are not going to look into that result anyway, since you wanted to 'write' you are unlikely to care about the result, and now the error is effectivelly suppressed. Not very offensive style.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      In the case of a memory or stack overflow, your process will probably just crash and be restarted by a supervisor.
      And for writing files, that's fine. You're still allowed to choose to ignore errors but at least you know they're there and may crop up.

    • @MarcoServetto
      @MarcoServetto Месяц назад

      Thanks for answering, I hope we can have a constructive chat.
      @@IsaacHarrisHolt 'you are still allowed to choose to ignore errors' Sure, this is what we did with old C error code as returns of functions.. and everyone ignored them. Note that I'm not pushing for standard exceptions, I'm just saying that optionals/eithers are a step back, and that we need some way to make sure that errors are explicitly handled in one way or another at all times, not just when we care about the operation result.
      - Depending on how 'crash and be restarted' works, .... it looks a lot like a try-catch.
      - Offensive programming is really the right style in my opinion, but it makes perfect sense to do it in layers.
      Where logically you have a few layers of master/slave patterns, where the slave computation can die, and the master computation can decide what do to, somehow protected by any corrupted state that the slave computation left behind. If 'crash and be restarted' works with actual processes, well.. that is too limited and relies too much on the OS, in my opinion.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      In this case I'm referring to BEAM processes, which aren't tied to the OS. Erlang has the concept of supervisors which are essentially processes that look after other processes (I have a video on this if you're interested).
      As for ignoring returned errors, sure, you could do that with C etc., but in C, when you DID want to look at the error, it was a lot more difficult to figure out what actually went wrong. You'd be relying on the author of the function documenting what int means what in some way, and different authors would have different standards for that.
      Gleam makes this trivial by allowing tagged unions types as the error type, so you easily get the type of error and any context attached to it.

  • @anotherelvis
    @anotherelvis Месяц назад

    Can you print a stack trace in gleam?
    In a debugging situation I would often like to log an entire stack trace for each error, so that I can fix my code.

  • @nand3kudasai
    @nand3kudasai 25 дней назад

    Ive been using return values as errors since veny long.
    Its not new, i think this was the norm long time ago.
    I do it In a sort of 'defensive programming' way.
    It doesnt depend on any special language feature. And i do things a bit differently than you.
    Im quite proficient with exceptions, but i like my way better.
    And i think it works really well.
    I make games with unreal, and (besides exceptions not being available) a critical error can easily crash the game. With my approach the crashes are only due to very low level stuff on the engine. And i usually patch those cases.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  25 дней назад

      Yes, errors as values have been around for a long time. That said, certain type systems make it easier. For example, C errors are typically just ints, meaning you have to then use some sort of lookup table to figure out exactly what went wrong in any meaningful way.

  • @moistness482
    @moistness482 Месяц назад +1

    Errors as values are the way it was always meant to be

  • @TheCheD3
    @TheCheD3 Месяц назад +2

    Im using vanilla HTML/JS for a project right now and I just want my dear sweet errors as values back. Gleam looks great, i might try it for my next project

  • @CTimmerman
    @CTimmerman Месяц назад

    So instead of a try catch block catching explicit errors, use "use" for that block's length plus a switch or whatever later on. Seems the same with extra steps.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      The difference is that try/catch is pretty much always optional. At least with `use` you're forced to write it in, acknowledging there's an error possibility.

    • @CTimmerman
      @CTimmerman Месяц назад

      @@IsaacHarrisHolt Then every function call should have 1+ line of error handling. How horrid.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      That's not horrid. That's... the point. It forces you to think about all the ways your program can fail

    • @CTimmerman
      @CTimmerman Месяц назад

      @@IsaacHarrisHolt While custom ECC and triple redundancy to withstand cosmic rays might land me a job at ESA, i just want to parse some JSON. A crash is fine. Does Gleam even have a formal prover like Spark Ada though?

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I feel like you're being deliberately obtuse now 😅 as mentioned in the video, you can still choose to crash if you want

  • @dissident1337
    @dissident1337 Месяц назад

    No. A function should only be expected to intentionally do what it says it does. An Exception is an exception.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Yeah, and with errors as values, we're just saying that something could go wrong :)

  • @Bwmat
    @Bwmat Месяц назад

    So, does anyone who uses C++ & wants their process to survive new failing actually do this in practice?

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I'm not sure I understand the question

    • @Bwmat
      @Bwmat Месяц назад

      @@IsaacHarrisHolt Sorry, it's not quite relevant to your video, other than the idea of eschewing exceptions in favour of error values.
      I spend most of my time writing C++, and in that context, I see people recommending such a coding style, but I've never been given a satisfactory answer as to how to do that in that language without it becoming extremely verbose.
      The reason is that for my purposes, I want to write code that goes into a shared library that gets used by external programs. In that context, I never want to crash or corrupt the process (except for doing the former if I detect the latter), so that means I _do_ want to handle out-of-memory conditions (i.e. `new SomethingImpl()` throwing std::bad_alloc or `new (std::nothrow) SomethingImpl()` returning nullptr) by gracefully unwinding the stack, and then returning an error code via the C API that the shared library is called via by the host program, not simply assume that it can never happen, or just calling exit(1) or abort() or something, which is what I usually hear suggested.
      The problem is that almost EVERYTHING other than 'mathy' functions probably needs to allocate internally to do its job, at least sometimes, and that allocation _can_ fail, so it makes almost EVERY return value an std::optional or something equivalent.
      By writing my code using exceptions for error handling, and 'simply' assuming that ANYTHING (other than a few well-known things which are _documented_ to not throw) can throw, and using proper RAII everywhere possible and a 'transactional' style, I find that doing this is relatively straightforward without polluting too much of my code.

  • @francoramiro7
    @francoramiro7 Месяц назад +3

    Amazing video! A super clear explanation🙌🏼🙌🏼🙌🏼

  • @radumotrescu3832
    @radumotrescu3832 Месяц назад +1

    Seems like older C/C++ API's got it right the first time. We use a lot of async boost in our server application and we just default to the error_code version of the methods unless we specifically need to handle exceptions. Its much easier to reason about an error code, especially in callback based code.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      100%, but it's also nice to have named errors in the form of enums!

  • @Hersatz
    @Hersatz Месяц назад

    I never understood the reason to catch errors.
    If there is an error in the system, the code shouldn't catch it and handle it in some weird workaround ways. The code should be fixed so that the error cannot occur anymore.
    When is a try catch actually truly useful and not due to bad code?

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      There are always things that can go wrong that are outside of your control. You might be trying to open a file that doesn't exist, or your internet connection might drop out in the middle of a HTTP request, or a million other things. You can't prevent every error

    • @MrDarkoiV
      @MrDarkoiV Месяц назад +1

      Exceptions are for code that cannot be handled locally. If you encounter out of memory error, you don't want to handle it in caller functions. You want to unwind stack until handling lack of memory makes sense.

    • @MadsterV
      @MadsterV Месяц назад +1

      exceptions are for...... exceptional situations!

  • @namef
    @namef Месяц назад +2

    Great video! (it came out 5 minutes ago and theres no way I could've watched it all)

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      Haha thank you! But 2x speed would work 😉

  • @FernandoDanko
    @FernandoDanko Месяц назад

    Hello undefined! You have NaN new messages.

  • @mh-tr5fb
    @mh-tr5fb Месяц назад

    pardon me, the pink smiley star... is it for a language?

  • @gerwazy373
    @gerwazy373 Месяц назад +1

    3:35 Bubbling the error is very common in functional languages. Most (if not every?) of the error types are monads, which means that you can define a way to compose functions on those types, and if an error value occurs, carry it to when it's matched on.
    Edit: Ok it's described on 5:04, nice ;)

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Yeah, you'll frequently pass it up, but it doesn't just magically appear three functions up the call stack like it would with exceptions :)

    • @gerwazy373
      @gerwazy373 Месяц назад +1

      @@IsaacHarrisHolt It works differently under the hood, but the result is very similar - a nested function can return an error but you only have to match at the very top, just like you would do with `try catch`.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Of course, but with errors as values you have to explicitly pass it back up the call stack. With exceptions it just kinda... appears at the closest try/catch that matches it

    • @gerwazy373
      @gerwazy373 Месяц назад +1

      @@IsaacHarrisHolt No, you don't have to do it explcitly, that's what the 'use' keyword does for you.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      You're still passing it explicitly. `use` is just a language feature like Rust's `?`. You're still making the choice to say "I have this error but don't want to deal with it, so the caller can have it now". It's still explicit

  • @smokingant
    @smokingant Месяц назад

    Issac is to Gleam what Akademiks is to Drake

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I'm hoping this is a good thing 😅

    • @ahuman32478
      @ahuman32478 Месяц назад

      Isaac is to Gleam what NoBoilerplate is to Rust

  • @nand3kudasai
    @nand3kudasai 25 дней назад

    Why is this video tagged as python lol?
    Exceptions are special values on certain languages.
    A lot of the tactics you use are pretty similar to how exceptions.
    I think the satement that youre saved from unhandled errors is not accurate.

  • @cosmochaosmaker
    @cosmochaosmaker Месяц назад +2

    Using errors as values instead of exceptions feels like trying to reinvent the wheel, but using triangles instead. 🤷‍♂️
    Exception handling is crucial for creating robust and maintainable applications and libraries, particularly in environments that have evolved over many years or decades. In such environments, development errors are more likely, especially when incorporating dynamically linked third-party code at runtime. Exceptions provide a structured and standardized way to handle unforeseen situations that can arise in these complex systems.
    While using errors as values can be appropriate in specific scenarios, such as in performance-critical code that has been thoroughly tested and is unlikely to encounter unexpected issues, it is not a one-size-fits-all solution. Exceptions generally offer a more reliable and maintainable approach for the majority of applications, enabling better error propagation and handling.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      I agree with some points here, but I disagree with exceptions handling being more readable. If I have to go function hunting to find where a particular exception is coming from, I'm having a bad time.
      Also, in most languages, you lose type safety when dealing with exceptions, which negatively impacts your ability to correctly identify and handle errors as they arise, leaving you playing catch up while your software's in production.

    • @cosmochaosmaker
      @cosmochaosmaker Месяц назад +1

      @@IsaacHarrisHolt That is mostly true, but what I wanted to say was that a hybrid approach will get the desired result.
      Combine exceptions and error return types for robust error handling. Use exceptions for critical infrastructure and initialization errors that require halting the system, ensuring immediate resolution and preventing instability. Employ error return types for business logic and non-critical errors, explicitly managing and propagating them to enhance type safety and predictability.
      This hybrid approach balances the concise control flow of exceptions with the explicit error management of return types, optimizing performance and reducing complexity. Clearly define boundaries for each method, document guidelines, ensure consistent handling, and implement thorough testing. This strategy supports maintainability, type safety, and efficient error management in complex systems.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      That's basically what Gleam does with a panic. It's not an exception, because you can't catch it, so it's only used to stop the program. I wouldn't say that's a hybrid model.

    • @Tuniwutzi
      @Tuniwutzi Месяц назад

      @@IsaacHarrisHolt I don't see how the erros as values are solving the "function hunting" problem. You say in the video that most of the time, you'll just be propagating errors up to the caller, which I agree with. But then, a few layers up, when you actually handle the error - how do you know where it originated? You have to go function hunting as well, no?
      Maybe it'll be a specific type that you can associate with a particular function. But then, if you call multiple different functions, how do you propagate all of their different error types upwards? There would have to be some common error type which can hold all of the specific functions error types. And then we're back at the question: how do you find the function this error came from?
      This weakens the type safety argument as well. With errors as values, if a function wants to propagate errors up, it'll need to use a common type for all errors that could occur within it. In many exception-based languages there is a common base class/interface that's enforced for all exceptions. Is there really such a big difference in type safety?

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +2

      There's a huge difference in type safety. It's unlikely that you'll just have a single error type that everything uses - if you look at the stdlib, you'll see that the functions there generally have their own special error types, and that's generally encouraged.
      I see your point about the function hunting argument, but you also know that the functions above will HAVE to handle the error in some way, even if that is just returning it up the call stack. With exceptions you don't get that guarantee.
      When you're writing a function that throws an exception, you're making the assumption that everyone who then calls that function will handle the exception somewhere and it won't crash the program.
      With errors as values, you're FORCING callers to handle the error somewhere.

  • @electra_
    @electra_ Месяц назад +1

    Wow that use statement is kinda sick

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      It's great! You can do all sorts of things with it

  • @chiubaca
    @chiubaca Месяц назад

    I wanna learn gleam cos the star looks cute

  • @Kokurorokuko
    @Kokurorokuko Месяц назад

    We are back in the same spot in history where we have error codes like in C++ era. But honestly, Error enums sounds like the best of both worlds. I always hated try-catches.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      Agreed! Handling random integer errors was a bit of a nightmare

  • @mendelovitch
    @mendelovitch Месяц назад +1

    The gifs are unbearable. Hard to watch. Please just show code.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      There isn't necessarily code for everything I say, but I'm trying to reduce the number of GIFs. That does mean the videos take longer to make, though 😅

    • @mendelovitch
      @mendelovitch Месяц назад

      @@IsaacHarrisHolt Sorry. I am being a drag. I enjoyed the video otherwise! You're good! It's just that these things get old so fast. Life as a meme is short and brutish. Then again, maybe someone else would love these things. You can't please everyone all the time. I don't know. Just do what you love and serial moaners like me can go fork themselves with rusty kitchenware!

    • @nand3kudasai
      @nand3kudasai 25 дней назад +1

      Some get really distracted with stuff like those gifs. Im one of those.
      I end up not even watching the video.
      I rather see nothing if theres nothing that adds information that i need to watch.

  • @_a_x_s_
    @_a_x_s_ Месяц назад +4

    Well, it appears more likely Rust to me...

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Gleam has some similarities, for sure!

    • @katech6020
      @katech6020 Месяц назад

      Gleam itself is written in Rust

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      True! There's definitely some inspiration

  • @lesarXD
    @lesarXD Месяц назад

    5:05 "its just a function, no special syntax required"
    5:19 "luckily for us, gleam has a special syntax"
    edit: @ 5:50 this is literally just (imo) uglier ? syntax. ill stick with a singular way of declaring variables

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Use isn't for declaring variables! It's for flattening out callbacks :)

  • @NickCombs
    @NickCombs Месяц назад +1

    More libraries for syntactic sugar... nah I'll pass. I'm good with wrapping functions in try-catch

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      No libraries here (except Snag), all pure Gleam!

    • @samuraijosh1595
      @samuraijosh1595 Месяц назад

      No this is provided in the base for gleam and many functional languages like Haskell, Ocaml.

  • @bcpeinhardt
    @bcpeinhardt Месяц назад

    Superb production and delivery. Writing was decent 😉

  • @dayvie9517
    @dayvie9517 Месяц назад

    Have you ever heard of logging? 😂

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Sure, what about it? Logging complements good error handling - it doesn't replace it.

  • @lankymoose1831
    @lankymoose1831 Месяц назад +2

    Tom is indeed a genius :D

  • @white_145
    @white_145 Месяц назад

    everyone gangsta until Optional option = null

  • @gotoastal
    @gotoastal Месяц назад

    The future? Sum types for success/error cases has been the standard in the ML family for decades.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      Yep! And the rest of the industry is finally catching up 😅

  • @ledkicker2392
    @ledkicker2392 Месяц назад

    So, do you mean the widely known and traditional C-way?
    int err = getaddrinfo(SERVER_HOSTNAME, SERVER_PORT, &hints, &result);
    Not sure why the need to present it in a light of a new language

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      The C way works, but it's nicer to have named errors in the form of enums rather than having to try and decode an int

    • @samuraijosh1595
      @samuraijosh1595 Месяц назад +1

      C is missing the monadic syntax sugar shown at 5:53

  • @vercolit
    @vercolit Месяц назад

    Imo, exceptions are my main pain with C++. The langage spec is convoluted and over complex due to the fact that it's a systems language from the 80s that needs backwards compatibility with C and its past self? Sure. Does it have 5 ways of doing almost exactly the same thing? Sure. My main problem is that error handling is awful. You have no real way of knowing if a function will throw or not (most functions can't be nothrow due to memeory allocation, and most people don't mark it anyways). That opens so many possibilities of forgetting to check an exception and it randomly crashing your program. It's really annoying, especially since it's pretty nuch the only way to error handle from a constructor (yes factory methods are a thing, they're not widespread and have issues)

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Exactly! This is the problem that errors as values solve. I know C technically uses ints for errors, but having a named enum is far better

    • @jackzugna5830
      @jackzugna5830 Месяц назад +1

      I think try-catch was born to pay more attention to the code that can crash everything, in pure C many call functions without managing the return value, the compiler will use a temporary variable and will not give any error at compilation time, the program then crashes and nobody knows why. C++ try-catch should help to pay more attention to the code and specify the error better, in addition to a maintenance issue: you can use ctrl+F to search for "try" instead of having to create a common standard for error variables.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      I don't think try/catch works, though. Sure you can search for where it's happening, but that's not helpful if you don't know where errors are coming from.

  • @aLfRemArShMeLlOw
    @aLfRemArShMeLlOw Месяц назад

    Tom's a genius.

  • @jakedewey3686
    @jakedewey3686 Месяц назад

    I just want something like Python but with Rust's type system and exhaustiveness checking, so error handling without exceptions becomes idiomatic.

  • @oof-software
    @oof-software Месяц назад

    The Inner JSON Effect mentioned

  • @chrism6880
    @chrism6880 Месяц назад +2

    just make every function return a monoid

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      I'm not smart enough for that 🤯

    • @Axman6
      @Axman6 Месяц назад +1

      You literally use monoids every day, we all do, (numbers,+,0), (numbers, \*, 1), (lists, append, []), (bool, &&, true), (IO, >>=, return)
      _(ok that last one may be a little controversial)_

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      I haven't written enough white papers to know what a monoid is 👀

    • @samuraijosh1595
      @samuraijosh1595 Месяц назад

      ​@@IsaacHarrisHoltat 5:53 you have written a Maybe Monad computation. So you're already good 😂

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Awesome!

  • @soyitiel
    @soyitiel Месяц назад +2

    C has always done it like that

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +3

      Ah, but in C you have to try and decode what all the different integers mean

    • @soyitiel
      @soyitiel Месяц назад

      ​@@IsaacHarrisHolt True.
      Still, Go's and other languages and paradigms way of handling errors by returning error values is a lot like how C does it, it even seems like Go was inspired by C in that way. So, moving away from exceptions and back to handling errors as values isn't really something new, it's just getting back to basics.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Yes absolutely. The industry is finally realising that we were nearly there all along. Gleam is nice because you have the enums, though, which helps a tonne

  • @Onyx-it8gk
    @Onyx-it8gk Месяц назад

    I heard an experienced developer say that Gleam's match is better than Rust's!

  • @ringpolitiet
    @ringpolitiet Месяц назад

    Skip the memes please.

  • @yoshi_mel
    @yoshi_mel Месяц назад +1

    erlang is offensive, got it

  • @mks-h
    @mks-h Месяц назад +2

    I don't like how you present all of this in a way to only ever mention Gleam, and not any other programming language. Especially at 2:35 - the "other language" is literally the language Gleam's compiler is written in. Yet, you go out of your way to avoid ever mentioning it, despite most of the features presented being literally copy-pasted from it. This is dishonest.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Feedback taken, thank you! But I am a Gleam RUclipsr 😉

    • @mks-h
      @mks-h Месяц назад +1

      @@IsaacHarrisHolt yes you are, but a) this video is advertised without any indication that it is Gleam specific, and b) while I understand not wanting to repeat "like in Rust" in almost every section, not saying it what-so-ever, or substituting it with "other language" is even worse.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      But it's not just Rust. I said "modern programming languages", and showed Go, Zig, Rust and Elixir. I don't specifically call out Rust or Rust-specific features at any point. I believe I mention that you might be familiar with the `Result` type from other languages, but it's not like Rust invented that type or that idea - it's been common in functional languages for decades. Do I have to call out EVERY language that has ever used a `Result` type to encapsulate errors?
      Also, fair on the packaging point, but you're probably seeing the 1 thumbnail variation of 3 that doesn't include the Gleam logo, so I've now got some extra feedback on that experiment. Thanks 🙂

    • @mks-h
      @mks-h Месяц назад

      @@IsaacHarrisHolt to be clear, I wasn't arguing about the "modern programming languages" part, although now that you mentioned it - I didn't really read that code on screen, and for sure didn't recognize it as four different languages. Putting their logos near the respective code snippets would have been great.
      As for the "Result" type, I repeatedly hear "from other language" in singular, although without a particle. So... My bad for assuming the worst, I guess. And I really cannot hear the plural "-s" there, no matter the volume.
      I guess the argument is resolved now, sorry for throwing accusations (I repeatedly can't remember not to do that, when I'm right about to do that).

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      No worries! It happens. And yeah sometimes the S gets cut off - I really need to look into my audio settings!

  • @SnowDaemon
    @SnowDaemon Месяц назад +1

    👍

  • @michaeltennant8312
    @michaeltennant8312 Месяц назад +1

    We have successfully returned to ideas from the C programming language and decorated them as something new once again! To be fair, while the idea isn't exactly new, gleam definitely executes the idea better then C does in regards to ease of development.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      100%! Not having to handle a million different ints is helpful, and the fact we can have named errors makes life much easier.

  • @TheTmLev
    @TheTmLev Месяц назад

    fucking hell how much budget does gleam have for all this marketing...

    • @bcpeinhardt
      @bcpeinhardt Месяц назад

      Like $0, we just like it

    • @bcpeinhardt
      @bcpeinhardt Месяц назад

      This was funded though lol but very little sponsorship currently to my knowledge

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +2

      As Ben said, none! It gets covered because it's a great language.

    • @TheTmLev
      @TheTmLev Месяц назад +1

      @@bcpeinhardt I'm just extremely surprised that almost every tech channel suddenly started covering Gleam in their videos

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +2

      It's because Gleam is new, exciting and fantastic! :)

  • @CyborusYT
    @CyborusYT Месяц назад

    ah yes, Other Language, my favorite

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Yeah, probably coulda named those in hindsight

    • @CyborusYT
      @CyborusYT Месяц назад

      @IsaacHarrisHolt ah I found it pretty funny! It was clear what Lang you meant

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      Actually, there are a lot of langs that used `Result` before just Rust, so it wouldn't have been great to name just one. I would have had to name all of them, which might've taken a while 😅

    • @CyborusYT
      @CyborusYT Месяц назад

      @@IsaacHarrisHolt Sure, but most people did hear about it _from_ Rust, even if they didn't invent it

  • @Gokhan-er8qv
    @Gokhan-er8qv Месяц назад +2

    Stop doing stop doing videos..

  • @iflux8821
    @iflux8821 Месяц назад

    I found a way how to deal with your memes - 2x 😌

  • @PinikRahman
    @PinikRahman Месяц назад +5

    Would love to see more code and less memes. Constantly repeating gif memes are cringe and distracting tbh

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Working on improving the stuff-to-gif ratio, but I'm always low on time so need something I can edit quickly!

  • @OmerFlame
    @OmerFlame Месяц назад

    I honestly don't like this kind of videos. Don't tell me what to do, if throwing errors works, then it works. I won't stop doing what I know works (and works well at that)

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      It's an opinion video! You don't have to agree with the concepts, and you're allowed to have your own views

  • @azaleacolburn
    @azaleacolburn Месяц назад +12

    Just use Rust

    • @adriancruz2822
      @adriancruz2822 Месяц назад +4

      High effort engagement here

    • @demarcorr
      @demarcorr Месяц назад +1

      No.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +3

      Gleam and Rust solve very different problems. They're not interchangeable

    • @azaleacolburn
      @azaleacolburn Месяц назад

      @@IsaacHarrisHolt I fundamentally don’t agree, but get where you’re coming from.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +3

      They have some overlap (e.g API development), but you'll have a much better time building a distributed, multi-threaded system in Gleam, since that's what the BEAM is built for.
      Similarly, you'd probably get along better with Rust if you're writing an operating system or something low level. The languages have different design goals

  • @apestogetherstrong341
    @apestogetherstrong341 Месяц назад

    gleam is stinky doodoo defensive programming. Erlang doesn’t need static typing. Also Gleam runs on BEAM but didn’t adopt OTP. Trash.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      I think you'll find that Gleam did, in fact, adopt OTP. You can check out the gleam_otp package to find out how wrong you are :)

    • @apestogetherstrong341
      @apestogetherstrong341 Месяц назад

      @@IsaacHarrisHolt Have you read the README for the same gleam_otp package you mention? «This library does not currently replicate all of the Erlang/OTP functionality».
      Just use elixir. It's alive and well. "Type safety" cancer is not what is needed for distributed systems. Joe Armstrong would've sighed with disappointment if shown this idiotic language with no point whatsoever, with no contribution whatsoever to the BEAM ecosystem or the distributed programming experience. It's not even a lisp. Cringe.

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад +1

      Of course gleam_otp doesn't have feature parity with Erlang. It's a much younger language. Thinking it's going to be exactly the same is naive.
      And on your point about typing, yes it introduces some challenges, but it also fixes a whole host of issues you would otherwise see in your programs, distributed or not.
      I would implore you to actually try Gleam for a reasonably-sized project and see how you like it. Badmouthing something without trying it is close-minded.

    • @apestogetherstrong341
      @apestogetherstrong341 Месяц назад

      @@IsaacHarrisHolt I have erlang and elixir experience, and I tried gleam. It's nothing new. Elixir fixes many aches with erlang, including type aches if there are some in your case. And it has macros. What is the availability status of modules that contain behaviours and macros from other beam langs? Does gleam still need the same fat layer of glue code to fix its inherent incompatibility with libraries that contain too much "let it crash"?

    • @IsaacHarrisHolt
      @IsaacHarrisHolt  Месяц назад

      Gleam is absolutely okay with "let it crash". It still supports supervisors, process monitoring and most of the requisite stuff there.