Again, the cool thing is that it's the same syntax no matter what kind of Monad you're in, so you can totally change the behavior of a monadic function by just using it with a more specific return type (at least in Haskell, other languages might make you pass around an implementation) Monads are a neat tool of composition, but composing monads themselves is fiddly and cumbersome, involving monad transformer "stacks" of deeply nested generic types. Monad transformers are really just not fun.
I'd give specific examples, but I'm kind of lethargic from the burrito I had for lunch. If you're familiar with flatMap(), a Monad is anything that's "flatmappable" (which in JS is just arrays, but languages like Scala take it much further). In C#, it's IQueryable (LINQ is a monad!)
Mind you, shell pipes aren't actually implemented monadically, but you can certainly visualize it that way when working with functional libraries.
No, it doesn't. Monad implementations may call the provided function zero times, or multiple times. Chaining implies exactly once, one of the common misconceptions about the monad interface.
Flatmap is another common attractive nuisance; flatmap is the monad implementation for lists but is not "monad" in general, any more than "an iterator on linked lists" is "iterator" in general.
Haskell is one of the few languages that lets you write those general purpose libraries. In other languages, there's really no point talking about monads.
It is a concept from category theory, which is the branch of mathematics that deals with saying as little as possible about as much as possible.
You don't have to worry about any of this unless a teacher tells you to. (And if they do, you can just leave.)
Like all abstractions, if you squint hard enough, different things can look the same. Monads can make optionals, lists, error handling, i/o, continuations, state manipulation, reading values, writing values and many other things look the same.
I don't think the general concept is that interesting, at least in programming. Monoids, though simpler, are probably way more interesting (mapreduce, caching, etc)
Applicatives were dusty theory when I learned Monads, so they never burned themselves into my consciousness. I'm doing good with Monoids tho.
I'd say monoids are more interesting, mainly due to implications for massively parallel algorithms and caching and how finding operations that fit the laws let you reap massive benefits in both aspects.
I've yet to see something as practically interesting for monads. Perhaps monadic parsers or STM qualify. What would you say makes monads just as interesting in a practical sense?
For example, an Option<T> could force you to unwrap the value and deal with errors. In true functional languages and/or where pattern matching is used, you must handle both cases, presumably creating a chain of "monads" all the way down (as most of your functions will return Option<T> or Result<T, Err> type patterns, since you can't throw an exception, and any value might be empty/error, so you have to propagate it upwards).
Honestly its a fancy term that functional programmers use to cow the laity. Its a bit like calling an if statement "probative procedural data query with indeterminate flow control."
Yeah, but "Monad" goes in the opposite direction, two syllables to sum up the whole deal. Also pretty typical for functional programmers :)
https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/b...
Beyond that, they're a pretty general and reusable way of looking at data structures that nest.
So, yes, I suppose.
Monads are types that let you build chains of lambdas, so you can mix control flow and variable scoping with the some type-specific logic.
Async IO/streams/optionals are monads. It's no accident that these could be programmed as nested callbacks/nested loops/nested if statements. That's the nested chain of Monad->lambda->Monad->...
Monads work well with syntax sugar (think async/await), and some languages like Haskell can abstract over them in neat ways.
On a fundamental level, all these describe abstraction over what happens "between functions calls" and how values are passed around.
1. Some languages provide special syntax for exceptions and async io - in both cases "something" might happen between function calls - either an exception handler is called under certain constitutions (eg prints logs) or a kernel level callback is created and the program is suspended.
2. One generalization of this can be implemented using python generator functions / coroutines - you can build a call graph by invoking all functions in you callstack as "yield from func()", and use "yield Exception" to propagate errors and "yield Timer(1)" to ask an outer caller (event loop) to wait, or "yield Print(mag)" if we want to keep our functions pure and make the C event loop handle all the IO. You can also pass values down the stack via corp.send(x).
3. But it is a little clunky. Some langaues like Koka give you an ability to define arbitrary "effects and effect handlers" which is essential special syntax for how we were abusing coroutines in the previous step that makes differentiating and handling different "kinds things that we pass up and down the callstack" (exceptions vs timers vs prints) easier.
4. But some langaues do not have effect handlers but still want to do "custom arbitary custom things between function calls". That's where monads come in - they define a type for defining chains/trees of function calls and rules for how these threes must be iterativley unwrapped and wrapped back depending on how the wrap looks like. Eg instead of doing a(b(c)) you say unit(c) | b | a and describe how piping must be done it terms of types of values that these steps process. I hope it makes it clear how one could implement side effect IO, or exceptions, or async io that pauses by defining how to "unwap such piping". In principle, you abstract away control flow by introducing your own syntax for building function call trees and then rules for writing piping that works with such trees.
Monad guru please correct me if I am wrong.