Monads are everywhere... Maybe that's bad? by Till Schröder

Поділитися
Вставка
  • Опубліковано 15 чер 2024
  • Till discusses the use of monads as a pattern for handling effects and chaining functions, with examples from various programming languages like JavaScript, Haskell, and C#. Monads, which can be thought of as a way to overload the semicolon, provide a more flexible way to handle errors and chain functions with effects. However, monads have challenges, such as the requirement for having the same error type for monadic computations and the complexity of implementing them in some languages.
    Till also introduces the concept of algebraic effects as an alternative to monads, which can handle side effects and exceptions in a more user-friendly way but are more complex to implement. Despite the challenges, Till expresses his fascination with the history of programming languages and his enjoyment of experimenting with various languages.
    Till Schröder
    till.red/
    / till-schroeder
    Links, about haskell and monads:
    wiki.haskell.org/All_About_Mo...
    okmij.org/ftp/Computation/IO-...
    www.haskell.org/definition/ha...
    www.haskell.org/definition/ha...
    Algebraic Effects:
    overreacted.io/algebraic-effe...
    homepages.inf.ed.ac.uk/gdp/pu...
    arxiv.org/abs/1807.05923
    dl.acm.org/doi/abs/10.1145/33...
    Mentioned languages with algebraic effects:
    discuss.ocaml.org/t/ocaml-5-0...
    koka-lang.github.io/koka/doc/...
    www.unison-lang.org/
    Chapters:
    00:00 Welcome by Magnus Sedlacek
    00:39 Thanks Ada Beat for sponsoring the video stream
    02:29 Introduction of Till Schröder
    02:38 Introduction
    02:40 Let’s get practical: Checking an email address
    08:46 Alternative: a result type
    10:25 This is a pattern (a monad)
    12:36 What does this mean? Monads are a way to overload the semicolon operator
    15:08 Monads are everywhere (JavaScript, C#, Rust, C++)
    17:48 Why are monads everywhere?
    26:00 Maybe that’s bad?
    30:54 A possible solution
    33:40 Effect wish list
    38:27 Algebraic effects
    40:42 But how could algebraic effects be implemented?
    44:44 That’s it - a broad overview of monads and algebraic effects
    45:24 Questions
    Video sponsor - Ada Beat
    adabeat.com/
    If you want to spread functional programming and support the channel, buy something from the shop: funcprogsweden.myspreadshop.net/
    #funcprogsweden
  • Наука та технологія

КОМЕНТАРІ • 62

  • @danchatka8613
    @danchatka8613 Рік тому +32

    This is the most understandable explanation of Monads I ever heard.

    • @conando025
      @conando025 Рік тому +10

      Well then you must not have heard that monads are monoids in the category of endofunctors

    • @danchatka8613
      @danchatka8613 Рік тому +5

      @@conando025 Keep your demonic incantations far from me! ;-)

    • @Drezil
      @Drezil Рік тому +2

      @@danchatka8613 @Conando well... you have to be careful. If you chose the wrong tensor for your monoid in the category of endofunctors you end up with just an applicative and not a monad .. ;)
      I also had another good one .. but i can't get right anymore .. something like "Lenses are just profunctors over the Co-State (aka Store) CoMonad" or something..

  • @Verrisin
    @Verrisin Рік тому +6

    leave it to the guy who ~dislikes monads to give you the best description of what they are :D
    - Everyone else explaining them just seem excited they understood it. You don't truly understand something, until you see it's flaws and better alternatives, and are willing to take the full context in.

    • @wall0511
      @wall0511 7 місяців тому +1

      Very underrated comment. You cannot understand something until you see its flaws. Brilliant.

  • @Ancipital_
    @Ancipital_ Рік тому +12

    Aside from all the kind words from others, which I support, in particular I want to compliment you on the sound quality. I wish everyone had their audio quality this good. I have a hearing problem so usually I have to rely somewhat on subtitles. Not here, so I'm very happy. Much thanks, and also for the high quality presentation.

  • @crzdriver
    @crzdriver Рік тому +13

    You did a great job motivating the idea behind algebraic effects. Thanks!

  • @KurtMueller
    @KurtMueller Рік тому +10

    Good talk on explaining the fundamentals of monads. You're a good presenter!

  • @fullmaster9333
    @fullmaster9333 Рік тому +6

    On the beginning of the second part: in Rust there are libs for that problem such as 'anyhow' and others.
    I started using 'error-stack' recently, works pretty nice 😊

  • @rascio88
    @rascio88 Рік тому +2

    best content around this topic! well exposed and very interesting, thank you!

  • @FourTetTrack
    @FourTetTrack Рік тому +3

    Awesome presentation! There's so much to learn in technology, I love it.

  • @racerand123
    @racerand123 Рік тому +1

    Such a great and interesting talk. when talking about algebraic effects I remembered the Flix programming language, which does have some of that, but only limited to pure unpure, at least the last time I looked. But the idea Is really cool

  • @shamimunnaherrumi2078
    @shamimunnaherrumi2078 Рік тому +2

    BROTHER, YOU ARE THE BEST!!! You oooh really helped me!! THANK YOU VERY MUCH!

  • @shadamethyst1258
    @shadamethyst1258 Рік тому +1

    Very interesting talk, I'm glad I got to discover a few things with it!
    What's this language you mentioned at the very end? It sounded like "qbml" or "gbml", but neither me nor the youtube captions could quite catch that

  • @grawss
    @grawss Рік тому

    Nice. Love the balance between what is and what could be.
    I'm not a fan of exceptions, so in those cases where I'd want to return a value anyway, just return a different value? Or run a different function? Not sure about this use-case.

  • @agsystems8220
    @agsystems8220 Рік тому +2

    Surprised Lisp didn't come up. The central problem is that we really want a degree of control over how our code is compiled, so we sort of need a handle into the compiler. A monad is effectively a lightweight user defined language inside a prescribed language. While making these is pretty simple, making tooling that is able to combine them and manage them without any dedicated support from the underlying language will always be hell. Lisp is basically "write your own compiler", so will always be interesting for this sort of thing.
    As a side note; A list parser that takes a function as it's last element, and regards a semicolon as a non-consuming closing bracket is already quite interesting. Basically when you meet a semicolon you treat it as a closing bracket, but also backtrack and add another opening bracket when you hit what would be its partner. ( a; (b) ; c) would be equivalent to (((a ) (b)) c), as both semicolons track back to the bracket before the 'a'. The idea is that your program starts with an opening description of state, and each line describes a function on that state. The semicolon is partially being used to make it so the programmer doesn't have to keep track of how deep it goes, but it also lets the parser treat it as imperative code despite being pure functional.
    I have to say I fundamentally disagree with this on whether there should be a difference between 'map' and 'then'. When writing initial code I do not want to have to be thinking about corner cases, so I really don't want to be forced to think about whether a particular function can throw or not. Your solution later is better. I want to be able to query the functions and ask specific questions such as "do you throw", "do you allocate", "are you thread safe", "are you safe for user inputs". When I am looking over my code for thread safety, I don't want multiple keywords that only differ in terms of some other specific questions, as it is just noise. I want my language to support my IDE in telling me the details of what I have written, not test my memory of random details.

    • @shinigamibourne8445
      @shinigamibourne8445 Рік тому +1

      Moreover, surprised that CommonLisp's Condition system wasn't mentioned when he talked about continuations (around 44 min mark)

  • @zellator
    @zellator 7 місяців тому +1

    What is the presentation software being used here? It's pretty nice and so easy to accompany

  • @FourTetTrack
    @FourTetTrack Рік тому +1

    A mini comment about the audio. I feel the audio levels are not very consistent and go from quite soft to high to soft again. I'm not an audio expert, but I wonder if putting a compressor would help keeping a more balanced level.

  • @elatedbento
    @elatedbento 8 місяців тому

    Great presentation! ❤

  • @AndrewCodeDev
    @AndrewCodeDev Рік тому +2

    Great talk, I really enjoyed it! I have one comment -
    There is a caveat regarding the idea of "overloading semi-colons". In a strongly typed language, the "." operator on a type (or member functions, class functions... whatever really) requires a non-void type. To chain a statement, you're calling another "." operator on the returned type. The semi-colon delimiter does not necessarily have this constraint... for instance, you can follow up any semi-colon with a "goto" or "jump" statement in something like assembly or permitting languages. This is not a method called on a returned type.
    I'd say that the sentiment is half-right. It does overload the semi-colon operator given that the next statement is operating on the returned type from the previous statement.
    Anyhow, I'm nitpicking - this was a really good talk.

  • @Verrisin
    @Verrisin Рік тому +1

    hmm... algebraic effects. I've been designing a language as a hobby for years (unlikely I will ever make it) and this is pretty similar to ideas I had ... (among many other on my wish list)

  • @frozen_tortus
    @frozen_tortus 5 місяців тому

    In more practical way to explain monad, for a structure to satisfy monadic interface:
    - Value that's protected in some kind of wrapper. usually supported by function that convert value into wrapped context (lifting).
    - function that is able to operate on that value, typically called map (here called .then)
    - we need to have function to get that value from the wrapper, unwrap it. Typically called .fold, here call map.
    - and finally we need to be able to flatten nested monads, when map returns another monad, typically called flatMap. Typically you wan't this behaviour controlled, so you don't auto merging like .then do.
    What I particularly don't like in this talk is that speaker used somewhat unconventional naming, adopted my JS to explain monads. Particularly example of Promise, which is basically eager Task monad, with inverted executor arguments.
    You can definitely explain this structure without ever called it monad, then you just call it thing with A, B, C thingy. I think "functional bros" basically made everyone hate us, because of strange words that most people don't know what they mean, and we sound like an ashole for using them, infront of people who never heard about it.
    So I invite you that, maybe just use "functional patterns" instead, then explain in humane way what it is.

  • @epgui
    @epgui Рік тому +1

    Presentation starts at 2:15

  • @TankorSmash
    @TankorSmash Рік тому

    I think roclang does support 13.4 in the wishlist

  • @minecraftermad
    @minecraftermad 2 місяці тому

    35:00 effect wish list sounds a bit like what zig does with the explicit allocator passing, and try keyword.

  • @porky1118
    @porky1118 Рік тому +1

    26:36 Implicit generic sum types would probably help here.
    Example:
    fn domainFromEmailAddress(A) Result[B, DomainFromEmailAddressError]
    fn tldFromDomain(B) Result[C, TldFromDomainError]
    fn Result.then[Ok, Err](fn (Result.Ok) Result[Ok, Err] ) Result[Ok, Err | Result.Err]
    So when calling "Result.Ok(A).then(domainFromEmailAddress).then(tldFromDomain)" the return type would be "Result[C, DomainFromEmailAddressError | TldFromDomainError]"
    I wonder, if there already is a language, which allows that. Rust doesn't. Probably Haskell or OCaml.

    • @ChrisBouchard
      @ChrisBouchard Рік тому

      A believe you could actually do something like that in TypeScript. TypeScript has some pretty impressive and flexible union and intersection type features - because it needed them to statically describe the sorts of things people would do implicitly in untyped JavaScript.

  • @Kyler1Ace
    @Kyler1Ace 17 днів тому

    Really not a fan of the idea of "try->throw->catch-> try again inside the function that threw" idea near the end, that sounds like itd be extremely hard to reason through. Maybe resumes that affect the control flow of the thrower should explicitly be passed in as an argument and if they dont apply they go back up as normal and if they do they try the passed in thing

  • @robtherub
    @robtherub Рік тому

    I thought this was going to be about the Monadology by Leibniz, is this something to do with code it seems, hmmm

  • @ABaumstumpf
    @ABaumstumpf Рік тому +1

    If people at least would TRY to explain what a Monad actually is. Surprisingly this short description here is more detailed than many multi-paragraph long dense descriptions. When you look up Monads on say Wikipedia you will be left wondering why anybody would ever voluntarily torture them self like that.

  • @piotrkozbial8753
    @piotrkozbial8753 Рік тому

    "13.5. Allow continuing" - this is precisely what Lisp does with its "conditions".

  • @johannes.pilkahn
    @johannes.pilkahn 8 місяців тому

    Well presented, well structured. Allow me one nit on your characterization of (modern) C++ in 7.4: "C++ avoids monads as much as possible" Nothing could be further from the truth. std::expected corresponds to Rust's (and your) Result type. Clearly you are not familiar with Eric Niebler's std::ranges library, std::future, Bartosz Milewski's work or Hana Dusikova's Graph Based Update System.

  • @LewisCowles
    @LewisCowles Рік тому

    Does Olle ever attend this?

  • @Ancipital_
    @Ancipital_ Рік тому

    Maybe "good" and "bad" are concepts that apply to humans but not to machines?

  • @mgetommy
    @mgetommy Рік тому +6

    It's funny that by far the closest system we have to what he desires is java, the only downside being 'its annoying to write down all the exceptions".

    • @lubricustheslippery5028
      @lubricustheslippery5028 Рік тому

      It's also super annoying that you have to write all the try catch and through stuff when you write some quick and dirty experiment / tool for yourself. Then it's also about using the tool/language construct only in the right cases. You should return error codes (something monadic) for common errors you handle directly. Exceptions should more be used for exceptional stuff you through up several layers and give some error message.

  • @sho3bum
    @sho3bum Рік тому

    making delimited continuations more mainstream

  • @PierreThierryKPH
    @PierreThierryKPH Рік тому +4

    I wonder why he didn't mention algebraic effect libraries, there are already several of them just in Haskell.

    • @danchatka8613
      @danchatka8613 Рік тому

      Please name algebraic effect libraries you know of, especially for main stream languages (ranked in Tiobe's top 30).
      Did you try any of them? How well did they work?

    • @PierreThierryKPH
      @PierreThierryKPH Рік тому +5

      @@danchatka8613 I'm currently using Polysemy in production code, and Haskell is 39 on the Tiobe index. Personally, I only have experience using algebraic effects in Haskell. Tiobe is a weird metric for this because Scratch is there before Kotlin, Scala, Dart and Haskell… Still, within Tiobe's top 30, Scala (29) has effekt, Rust (20) has effing-mad, Ruby (16) has dry-effect and affect, Swift (15) has BowEffects, Javascript (7) has Effects.js and algebraic-effects, C# (5) has Eff, C(2) has libhandler and Python (1) has Eff.

    • @danchatka8613
      @danchatka8613 Рік тому +3

      ​@@PierreThierryKPH thank you for providing these starting points.
      tiobe has its fatal flaws. most languages i'd use are below 30 on the list.
      i'll look into Scala's effekt and Rust's effing-mad

    • @FourTetTrack
      @FourTetTrack Рік тому +1

      @@PierreThierryKPH Thanks a lot for sharing this!

    • @PierreThierryKPH
      @PierreThierryKPH Рік тому +1

      @@danchatka8613 If you like algebraic effects, you may want to take a look at them in Haskell, because there, purity means the compiler can guarantee you that your code with effect X has no other effects. I'm pretty sure most other languages won't make that guarantee.

  • @CristiNeagu
    @CristiNeagu Рік тому

    Functional programming is to normal programming what Jazz is to Rock. Change my mind.

  • @FTropper
    @FTropper Рік тому +1

    You almost lost me at "Java Exceptions are bad" - but I'm glad I sticked with you. Great talk, did learn a lot of new things!

  • @ianliu88
    @ianliu88 5 місяців тому

    He must see Roc lang

  • @LowestofheDead
    @LowestofheDead Рік тому

    So a better definition of a Monad is: _"A wrapper that allows you to use a chain of functions (any of which could fail) as if there was no wrapper. If a failure happens, the remaining functions aren't executed and the failure can be resolved after the chain."_
    *EDIT As others have pointed out below, this doesn't apply to all monads.*
    I like this definition because it tells you _why_ we use Monads, not just what their abstract definition is. All the properties of Monads are clear once you understand the purpose.
    It makes me realise that Message-Passing languages don't have this problem, because you can modify the receiving object to respond to failures.
    You don't have to change every calling function into a Try-Catch or an And-Then, or change the functions' type annotations. The object _is_ the Monad.
    I will say however - it's useful to know how a function could fail from looking at its type signature.

    • @spartanA01
      @spartanA01 Рік тому +4

      No, this definition is not correct. That's only for very specific data types like Optional or Result. Other monads do not have a concept of "failure" and have other purposes. For example, Reader which is used for some kind of dependency injection behaviour. Or something like "IO" which performs a side-effect like getting the current date.

    • @Ancipital_
      @Ancipital_ Рік тому +3

      Nice try though 🙂👍

    • @ChrisBouchard
      @ChrisBouchard Рік тому +1

      Just to add to what others have replied: this video makes it sound like monads must include an "early return" functionality, but that's not a requirement. E.g., lists/arrays as monads via map and flatMap have no failure or early return functionality. It's all about chaining and state.

    • @LowestofheDead
      @LowestofheDead Рік тому +1

      My mistake. You're right that the motivations for Reader and IO are very different to the 'safe chaining' in Option/Result
      To be honest, it's very unclear if there's any shared purpose between Reader/IO monads and Option/Result, only shared implementation. No one online can explain why they're grouped under the same concept.
      It's as if we met someone who has never seen a car, and explained that cars have 5 wheels.. but never that 4 wheels are for rolling and 1 wheel is for steering.
      It's not important that the wheels have the same shape and structure and name, their _purpose_ is more crucial.
      If Option/IO/Reader were invented today for the first time, would we group them under the same umbrella?

    • @Till.
      @Till. Рік тому +1

      @@LowestofheDead Yes, they serve very different purposes. And that is precisely why the term Monad isn't used that much outside of the pure functional world - every use case of Monads gets its own name. Also, languages like Haskell like to name things based on their structure rather than their purpose.
      There is an advantage to calling Monads by their name though, you can write code that operates on all Monads. To give a comparison, in C you can't implement a generic list. So every time you need to use a list of something, it get it's own special implementation. That makes it usually very explicit what the list is used for and how. It works just fine. But most people who have programmed in a language with generics are really annoyed when they have to go back to C and just want to create a list of integers, or whatever. It's the same operations every time, so who wants to implement them again. Also, in C, whenever you a new see list, you need to "relearn" it's interface. If you get an Vec in Rust, you immediately know what to with it.
      Similarly, some one can write things like monad transformers or special operators that work on all monads, and once you learned those you can use them everywhere.
      Now, I don't know whether languages "should" group all Monads together. Maybe it makes sense for some, but not for others. But hopefully this gives you a better idea why some people like to talk about Monads. :)

  • @MyAce8
    @MyAce8 Рік тому

    promises aren't monads

    • @yjlom
      @yjlom Рік тому +10

      are promises monads? let's check; a monad needs:
      to be a functor:
      a function m of form `Type -> Type`
      Int is a type, Promise Int is a type, it actually works for any input type ---> ok
      a function of form `(a -> b) -> m a -> m b` (often called 'map')
      do_when_information_becomes_available is an example of such function ---> ok
      to be a monad, it further needs:
      a function of type `a -> m a` (often called 'unit' or 'return') that preserves the wrapped value
      async, thread, go,... depending on the language may be examples of such function ---> ok
      a function of type `m a -> (a -> m b) -> m b ` (often called '>>=' or 'bind') that sequences calculations
      compose_async_when_information_becomes_available is an example of such function ---> ok
      so why would a promise not be a monad?

    • @vukkulvar9769
      @vukkulvar9769 Рік тому

      @@yjlom
      In JavaScript, the type of [ [ [ 42 ] ] ] is Array, not Array.
      Meanwhile, the type of Promise.resolve( Promise.resolve( Promise.resolve( 42 ) ) ) is not Promise, but Promise.

    • @chewbaccarampage
      @chewbaccarampage Рік тому +1

      @@yjlom In JS 'then' is overloaded, in that it behaves like both map and flatMap. I think they are trying to separate them though, but it's hard because so much code already exploits this overloading.