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

แชร์
ฝัง
  • เผยแพร่เมื่อ 16 ต.ค. 2024

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

  • @danchatka8613
    @danchatka8613 2 ปีที่แล้ว +32

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

    • @conando025
      @conando025 2 ปีที่แล้ว +10

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

    • @danchatka8613
      @danchatka8613 2 ปีที่แล้ว +5

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

    • @Drezil
      @Drezil 2 ปีที่แล้ว +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..

  • @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.

  • @KurtMueller
    @KurtMueller 2 ปีที่แล้ว +9

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

  • @daniel-wificidr
    @daniel-wificidr 2 ปีที่แล้ว +12

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

  • @fullmaster9333
    @fullmaster9333 2 ปีที่แล้ว +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 😊

  • @zellator
    @zellator 11 หลายเดือนก่อน +1

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

  • @agsystems8220
    @agsystems8220 2 ปีที่แล้ว +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)

  • @FourTetTrack
    @FourTetTrack 2 ปีที่แล้ว +3

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

  • @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 11 หลายเดือนก่อน +1

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

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

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

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

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

  • @FourTetTrack
    @FourTetTrack 2 ปีที่แล้ว +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.

  • @racerand123
    @racerand123 2 ปีที่แล้ว +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

  • @shadamethyst1258
    @shadamethyst1258 2 ปีที่แล้ว +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

    • @uolmir
      @uolmir 2 ปีที่แล้ว

      Cubiml

  • @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.

  • @Kyler1Ace
    @Kyler1Ace 4 หลายเดือนก่อน

    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

  • @frozen_tortus
    @frozen_tortus 9 หลายเดือนก่อน

    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.

  • @AndrewCodeDev
    @AndrewCodeDev 2 ปีที่แล้ว +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.

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

    Presentation starts at 2:15

  • @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)

  • @porky1118
    @porky1118 2 ปีที่แล้ว +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.

  • @mgetommy
    @mgetommy 2 ปีที่แล้ว +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 2 ปีที่แล้ว

      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.

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

    Great presentation! ❤

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

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

  • @minecraftermad
    @minecraftermad 6 หลายเดือนก่อน

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

  • @ABaumstumpf
    @ABaumstumpf 2 ปีที่แล้ว +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.

  • @LewisCowles
    @LewisCowles 2 ปีที่แล้ว

    Does Olle ever attend this?

  • @johannes.pilkahn
    @johannes.pilkahn ปีที่แล้ว

    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.

  • @robtherub
    @robtherub 2 ปีที่แล้ว

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

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

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

  • @CristiNeagu
    @CristiNeagu 2 ปีที่แล้ว

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

  • @TankorSmash
    @TankorSmash 2 ปีที่แล้ว

    I think roclang does support 13.4 in the wishlist

  • @PierreThierryKPH
    @PierreThierryKPH 2 ปีที่แล้ว +4

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

    • @danchatka8613
      @danchatka8613 2 ปีที่แล้ว

      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 2 ปีที่แล้ว +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 2 ปีที่แล้ว +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 2 ปีที่แล้ว +1

      @@PierreThierryKPH Thanks a lot for sharing this!

    • @PierreThierryKPH
      @PierreThierryKPH 2 ปีที่แล้ว +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.

  • @FTropper
    @FTropper 2 ปีที่แล้ว +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!

  • @sho3bum
    @sho3bum 2 ปีที่แล้ว

    making delimited continuations more mainstream

  • @ianliu88
    @ianliu88 9 หลายเดือนก่อน

    He must see Roc lang

  • @LowestofheDead
    @LowestofheDead 2 ปีที่แล้ว

    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 2 ปีที่แล้ว +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 2 ปีที่แล้ว

    promises aren't monads

    • @yjlom
      @yjlom 2 ปีที่แล้ว +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 2 ปีที่แล้ว

      @@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 2 ปีที่แล้ว +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.