Talk:Monad (functional programming)

Page contents not supported in other languages.
From Wikipedia, the free encyclopedia

Not useful for non-Haskell programmers[edit]

Almost any programming language nowadays has some form of monads and thus many people will come here first looking for explanation and find something completely incomprehensible to them. IMHO this page should completely eliminate Haskell from overview and explain terms in plain English and perhaps some JavaScript (or TypeScript since generic types are pretty useful in fully explaining it?) like pseudocode. — Preceding unsigned comment added by 207.96.108.12 (talk) 15:27, 21 June 2021 (UTC)[reply]

Hi there, the page could definitely still use more work so changes are welcome. I did a lot of editing a couple years back here, but mostly worked through my list of ideas; I just watch the talk-page because it's evolving & conceptual discussions still come up (plus I have one table for this page on my to-do list).
For the code snippets specifically, there is a pretty clear consensus to minimize Haskell on the page (outside of one or two examples, like IO, for flavor). The catch is there's no real guidance on Wikipedia for functional pseudocode, so when I was doing a lot of editing, I wound up settling on a pseudocode that still looks a lot like Haskell. The thinking there was that it is concise, abstract, de-emphasizes execution order, and often resembles functional math notation, especially if you leave things like lambda calculus implied. But there was also a bit of incrementalism in that decision. I left a long, rambling debriefing for my choices in the talk archives.
If you do have something else in mind though, I'd go for it. I think pseudocode as close as possible to basic math notation (mapping arrows, "f(x)" style functions, etc.) fits this topic well, especially for the (granted, probably very rare) non-programmers that stumble onto the article. So I lean more towards something that looks like Haskell than imperative code in this case, even if it's a popular language, but that's just me. --Zar2gar1 (talk) 21:20, 23 June 2021 (UTC)[reply]
I just rewrote the An Example: Maybe monad section and the introduction to use less haskell as well as being more clear. I used Rust for the code which is my preferred language and I think has similar syntax to other popular languages like Python & C++. I will try to do the rest if I have time. Any critiques? I'm a little new to Wikipedia editing. Zyansheep (talk) 05:44, 22 November 2021 (UTC)[reply]
Circled back to Graham Hutton's 2016 book to expand/generalize the Rust examples. They all work together to get beyond any H. syntax. Used Failure/ Success as generic concepts from (Hutton 2016) to show the role of bind in the languages used (H., Rust, F#, etc.) --Ancheta Wis   (talk | contribs) 05:09, 4 January 2022 (UTC)[reply]

We should dehaskelize it[edit]

There's a big problem with this article. It's all Haskell. That's not good. We have to be more abstract. Maybe write a bunch of paragraphs regarding how a monad looks like in Scala, Java, JavaScript, C++, Rust, C#, etc. Vlad Patryshev (talk) 19:27, 4 January 2017 (UTC)[reply]

Haskell is a huge contributor to the use of monads in functional programming, so it's logical that many sections are based on documentation written in it. (Some may say that writing in Haskell is being "more abstract"). We have examples in Javascript and F#. If you know of references that document the usage of monads in those other languages, by all means bring them here and we'll figure out the best way to include them in the article. Diego (talk) 09:55, 5 January 2017 (UTC)[reply]
A Haskell-oriented explanation would be OK if it also explained enough Haskell details used so readers needn't already know Haskell to follow along. Grokking monads is difficult so we can't really expect readers to simultaneously make lots of successful inferences about Haskell. This is doubly important since you can't get far in Haskell in turn without understanding monads.
Please explain the syntax used, also its precedence (or use enough parentheses to avoid that). Are newlines significant? What determines which monadic type's Just, return, or >>= get called in presented example expressions such as Just 10 // Just 5? Type classes, parameterized types, operator overloading, type inference, (), ... What's the relationship between Just, unit, and return? When the article says “a monadic value: M a”, is that really a variable, a type, or both?
An alternate way to explain monads would be in a more familiar programming language. Using an object oriented language would answer the question of which Just and bind get called, but it ought to be a language where types are first class values like JavaScript, Ruby, or Smalltalk.
That said, this page has improved dramatically over the last few years. 73.93.185.77 (talk) 01:55, 12 January 2017 (UTC)[reply]
There is a (26 Oct 2014) a 5-line, internally consistent, mental model of Haskell--Ancheta Wis   (talk | contribs) 00:30, 28 October 2022 (UTC)[reply]
I'm afraid explaining the syntax of Haskell is outside the scope of this article. What it could do is better identify what features of the language are used in each example (enumerated types, pattern matching, etc).
Adding examples in imperative languages wouldn't be a good idea, methinks; monads and object-orientation don't mix well, as the contrived Javascript example shows. IMHO one should have at least a basic grasp of functional programming before trying to understand monads; this is the scope where they are more useful anyway.
In imperative languages you can use mutable state, so one of the main selling points of monads for strict functional languages is not needed there. And the other uses, for handling data structures and control threads, would require a syntax in an imperative language too awkward to be useful. Diego (talk) 13:29, 12 January 2017 (UTC)[reply]
Maybe this could be a starting point to explain things: What would one do in an imperative language? Why is this not possible (or at least not that easy) in a functional language? In what way does the concept of monads solve this problem? 217.61.234.177 (talk) 07:37, 15 July 2020 (UTC)[reply]
Problem solving can take many forms; please see reframing (in the sense of denotational semantics) for one approach to problem solving. --Ancheta Wis   (talk | contribs) 13:48, 18 July 2020 (UTC)[reply]
How does this relate to the suggested way of introducing monads? 217.61.234.177 (talk) 08:08, 19 July 2020 (UTC)[reply]
Subjunctive mood and reframing are part of Haskell: as an example, let tmp::b = g x in f tmp can be read let tmp be g applied to x in f tmp where the compose operator ('∘') f g has type (b→c)→(a→b)→a→c [1]: minute 11:45 

References

be is "Subjunctive = " and part of the Haskell let syntax, as are in f tmp (ie, in f applied to tmp) and where, which serve to Reframe the terms of some expression which are used to define a function such as compose, for example. (The section ('∘') f g means f ∘ g, read as f follows g, and the a,b,c are polymorphic type variables, in Haskell. −It may help to mentally separate the concepts reduce, bind, eval, and apply in your further readings. The articles sometimes gloss over differences in these separate concepts when discussing them in examples. A-normal form can be a useful reduction.) Evaluation strategy#Call_by_need says "Haskell only supports side effects (such as mutation) via the use of monads".
--Ancheta Wis   (talk | contribs) 02:55, 9 August 2020 (UTC)[reply]
See Gabriel Gonzalez' Haskell for all (26 Oct 2014) How to desugar Haskell code for a 5-line, internally consistent, mental model of Haskell, and how to reduce/rewrite its expressions into their equivalents (which depends on how you intend to apply those expressions). --Ancheta Wis   (talk | contribs) 05:31, 4 January 2022 (UTC)[reply]

Uncertainty about the term's etymology[edit]

The clause "due to category-theorist Saunders Mac Lane" suggests that Mac Lane invented the term. This appears to be an exaggeration: Mac Lane may have popularized it (Categories for the Working Mathematician, 2nd Ed., p.138), but Where does the term “Monad” come from? documents ambiguity about the original source. This Week's Finds in Mathematical Physics (Week 200) credits Jean Benabou, whereas OPERADS, ALGEBRAS AND MODULES implies that J. P. May either invented the term or advocated it to Mac Lane before the latter published CftWM.

Mac Lane offers no definitive explanation of the name in CftWM, but his book is clearly the most reputable source among those I cited above. Absent an authoritative source, perhaps we should reword the "due to..." clause to highlight the ambiguity and more conservatively describe Mac Lane's role?

Monad (category theory) is similarly loose regarding attribution and should be clarified. Etymology? contains a brief but inconclusive discussion about the term's origin. — Preceding unsigned comment added by Maniaphobic (talkcontribs) 03:07, 27 June 2019 (UTC)[reply]

In the same vein ".. researchers working with Haskell eventually generalized the monad pattern" might be rephrased as ".. in the 2010s researchers working with Haskell eventually recognized that monads are applicative functors". (This leads back to Mac Lane as well.) page 138 of Categories for the Working Mathematician --Ancheta Wis   (talk | contribs) 06:05, 27 June 2019 (UTC)[reply]

List example[edit]

§ Another example: List claims • is bind, but then defines it as Kleisli composition, which is quite different. I also have no idea what ° is supposed to mean: is it the Kleisli star? But we don't really need to use it in this pseudocode (it's a trivial embedding of a subset of arrows in one category as arrows in another category - we're not discussing the category-theoretical underpinnings at this point) and "f° := unit•f = f•unit" is nonsense - "unit•f = f•unit" is a law for •, not a definition of °. Hairy Dude (talk) 16:54, 30 June 2019 (UTC)[reply]

Perhaps the editor who contributed it might defend it; otherwise I agree it can go. (Note: the citation to Dan Piponi's discussion motivates the contribution, for me.) --Ancheta Wis   (talk | contribs) 23:53, 30 June 2019 (UTC)[reply]
How about now? Just swinging by, but I've done a variation on the diagram similar to one I tried a while back. The original contributor didn't like my changes, and since this is just an occasional hobby / writing practice, I had no interest in an edit war. I don't think they're on Wikipedia anymore though, and they never responded to my points here on the talk-page.
As for the old caption, I think that was a misinterpretation of Dan Piponi's post. Since the ° is appended to the individual "sqrt" & "cbrt" functions, I think it was supposed to correspond to the ' in the blogpost. But that's just to distinguish the multivalued, complex functions from the simpler real ones; it can't be the monadic lift for List because you have to consider the individual function semantics to apply it.
And the • as bind was only correct in the 1st & 4th identity. Besides not matching up with the article text, I'm pretty sure the old caption was mixing up bind with Haskell function composition (f . g) when reading the 2nd & 3rd identity from the blogpost. --Zar2gar1 (talk) 06:13, 10 February 2021 (UTC)[reply]
The image has lost the parallelogram/rectangle syntax: parallelograms denote input/output (complex numbers or lists); rectangles denoted functions; input can only be fed into one function (can't feed one input into [cbrt] and [>>=cbrt]); [cbrt] should not accepts a list because it is not lifted.
The diagram should look something like this (rotated here for easier typing):
                 [cbrt°] ↘      ↗ < 8> → [cbrt°] → < 2,  2e...,  2e...> ↘
<64> → [sqrt°] → <8, -8> → [map]                                           [concat] → < 2,  2e...,  2e..., -2, -2e..., -2e...>
                                ↘ <-8> → [cbrt°] → <-2, -2e..., -2e...> ↗
...which represents this far less comprehensible equasion:
sxrt(64) = (cbrt°•sqrt°)(64) =  concat(map(cbrt°,sqrt°(64))) = concat(map(cbrt°,concat(map(sqrt,unit(64))))) = concat(map(cbrt°,[8,-8])) = concat(cbrt°(8),cbrt°(-8)) = concat(concat(map(cbrt,unit(8)),map(cbrt,unit(-8))) = concat([2,2e...,2e...],[-2,-2e...,-2e...]) = [2,2e...,2e...,-2,-2e...,-2e...]
Using concat (append), map, lift (°), unit, and bind (•) as defined in the cited source. I hope I got that right. CrossCriss (talk) 17:11, 19 April 2021 (UTC)[reply]

Hi there, so I think you make some good points about the flowchart, such as the boxes. My intent was actually for them to make a slightly more general distinction: rectangles for (first- or higher-order) functions that allow evaluating the expression further, parallelograms for the expression itself, including self-contained rewrites. That's not immediately clear though, and perhaps a couple more style variations would be good (for example, to point out that map is a higher-order function).

You're also right that cbrt (without >>=) only accepts a simple input, not the list. Putting cbrt with the list in that 1st parallelogram on the left, without further reduction, was meant to show the expression can't be evaluated until map is applied. That's not particularly clear though, so if you can think of a clearer way to put it, that would be great.

That said, like I wrote above, I think the ° (' in the original blog-post) should only be considered as a shorthand to distinguish monadic functions (like the use of x' variables in physics), not an official, valid lift. A few of his examples are derived by lifting, but if you notice in the multivalued trig example, he never formally defines how to go from the real-valued cube root to the multi-valued complex one. To do so requires facts about the underlying function (how to do complex roots), but the lift should be a property of the monad, which is only a generic list.

Or another way to look at it: in his first example with the writer monad (what he calls "debuggable functions"), when he does explicitly describe lifting a function f, he doesn't specify anything particular for a debug message. He only tags the function with the empty string because anything more implies knowledge of the function's specifics, but the lift has to be generic to the monad, which only specifies that comments will be appended. To specify what comment a monadic function should output, that needs to be defined along with the monadic function itself.

I'm still not sure it's correct to consider the • a bind either. One immediate issue is that it doesn't match a valid infix bind's type signature: one input should be a monadic value, not a 2nd monadic function (funny enough, IIUC in category theory terms, bind is itself a "lift" from (a -> Mb) functions to (Ma -> Mb) ones). Now if it's meant to be like the * from the blog-post, that's actually monadic composition (our articles uses >=> like in Haskell for now); that does have the correct type-signature, but it's not directly equivalent to bind or the composition of join & map. --Zar2gar1 (talk) 07:02, 20 April 2021 (UTC)[reply]

"Non-technical explanation" and the monad tutorial fallacy[edit]

The "Non-technical explanation" section was awful. I've removed the whole thing as it's actually more confusing than the technical introductory section. Monads are easy to describe once you know how they work using any number of analogies, but is there any reasonable way of describing monads to someone who hasn't already learnt the concept the hard way?

This blog post on the "monad tutorial fallacy" is I think insightful; there's really no substitute for doing the hard work of understanding, and once you've done it, your insight is still hard (impossible?) to communicate with others, because the hard work simply cannot be avoided. -- The Anome (talk) 13:25, 8 June 2020 (UTC)[reply]

Except that's only true in a limited way. Mathematics pedagogy has known for a long time that often the best way to get someone to understand something is to begin by describing concrete instances and only once the intuition has been developed for the concrete do we generalize and abstract. Think about how you learned about the real numbers. Did you start by sayings its the unique Dedekind complete ordered field or hand you a definition in terms of equivalence classes of Cauchy sequences? Of course not! You started by learning to manipulate concrete examples of reals, first the rationals and then you started to add some special definable real numbers like pi which you can approximate and then we say ok now it's like that but we generalize even to values which you can't define and give a fully formal definition.
I think the linked article does have a good point but I'd phrase it as this. When introducing a new abstraction it's important to be clear on whether you are giving a concrete example of a type of thing that falls under the concept or something that can kinda motivate the concept or if you are suggesting that a description captures the complete nature of the concept. Peter M. Gerdes (talk) 16:25, 2 October 2021 (UTC)[reply]

"f returns a defined value of type Maybe U"[edit]

One of the comments in >>= operator for Maybe says:

"f returns a defined value of type Maybe U".

Is that "defined" correct?

(It sounds like it's saying that that function call can't return Nothing. However, function f's return type is Maybe U, so can't the function return _either_ "a defined value of type Maybe U" _or_ the Nothing value of type Maybe U?)

100.36.43.9 (talk) 18:36, 6 March 2021 (UTC)[reply]

I think the main gist of the comment is correct, since it's placed with the 1st branch of the if statement. You're right though that it does sort of imply that Nothing wouldn't be a valid Maybe value, which isn't correct. I'll tweak the wording to emphasize it wraps a defined Just value within the Maybe type. --Zar2gar1 (talk) 23:11, 20 April 2021 (UTC)[reply]

Consistency in notation[edit]

The first section of the article seems to be using `:` for type declarations, e.g.

unit : T → M T

while the Analysis section is using it to indicate the definition of the function, e.g.

map φ : (a → b) → (ma → mb)

This is confusing - it would be good to rewrite the Analysis section to be more consistent with the intro. We could potentially write both the type and the definition:

map : (T → U) → M T → M U

map φ ma = ?


Also, in the Analysis section, I think we should explicitly state the types of ma, mmma, etc, to avoid confusion (I found it hard to work out). --Jordan Mitchell Barrett (talk) 21:11, 16 September 2021 (UTC)[reply]

The part that has been left unsaid in the article is that 'whitespace is an operator' .[1] Thus in the pseudocode in the article, whitespace can mean function application, or even more, depending on the language. The pseudocode is written in mathematical style, or in functional programming style (which dates back to Miranda (programming language)). --Ancheta Wis   (talk | contribs) 11:30, 28 October 2022 (UTC)[reply]
Hi there, first off, the : symbol is actually one bit I really didn't think through much. IIRC Haskell uses it for type signatures, and it's not that different from its use in plain English or function signatures in math. So I just ran with it without thinking too much about it; if some inconsistency jumps out at you, I don't see a reason not to change it.
As for the the use of ma in the Analysis section, I'm not sure I follow about making the types explicit. Do you mean include the signatures in an expanded form? I tried to stay concise, but if you think adding some details clarifies things, that's always an improvement.
It is a little tricky because the laws are pretty generic, as long as m is monadic and the same variable means the same thing on both sides of an expression. Repeating the variable for the monad type was the simplest way I could think of to express the nesting you see in the laws (and like in the List example, when you're in that transition state after mapping the monadic function but before reducing again with join). Zar2gar1 (talk) 21:24, 25 October 2021 (UTC)[reply]

IFF or equality?[edit]

Maybe I'm just being dumb bc I'm just a mathematician who doesn't do category theory and may be unfamiliar with some of the programming/category theory specific notation but shouldn't the statement of the Monad laws use an equals symbol not a double arrow (as it appears in iPhone Wikipedia app)? If it is correct as written it might help to add a note explaining that it's not meant to be read as the usual logical connective. Peter M. Gerdes (talk) 16:06, 2 October 2021 (UTC)[reply]

No, you're definitely not being dumb or missing some deep secret; while the choice was intentional, it was mainly a compromise for the pseudocode. I thought it could act as a (metalanguage?) hint that the monad laws aren't necessarily programmed, but ultimately logical propositions about a potential monad. They may or may not be true, based on whether the object is actually verified to be a monad.
If anyone wants to change it, I'm definitely not opposed. I'd just stay away from the single = sign since that's used for assignment in so many programming contexts, but a == or === is pretty common for equality in several languages. Ideally though, we'd still want to emphasize the laws (typically) aren't checked within the program, but outside of it logically. Zar2gar1 (talk) 21:24, 25 October 2021 (UTC)[reply]

Informal style[edit]

Occasionally I'm seeing sentences that read more like a blog post than an encyclopedia:

With just a little extra functional spice on top, this Maybe type transforms into a fully-featured monad.

Having to rewrite functions to take Maybes in this concrete example requires a lot of boilerplate (look at all those Just expressions!).

I'm not up on the latest Wikipedia guidelines, so I'm not confident about what is allowed. I'm curious about what the current thinking is from anyone who knows more about this topic. modify 15:16, 29 August 2022 (UTC)[reply]

Pseudo-code in the article[edit]

The meaning of the pseudo-code needs to be clarified:

(f >=> g) x = (f(x) → mb) >>= g(y = b)
1: My personal reading is that this is an equation. The left hand side is (f >=> g) x
2: The right hand side is
Let functor (f(x) → mb) be the functor resulting from substituting mb for y in g;
3: The (y=b) on the right hand side is not an equation; but g(y=b) is an application of g to the type y where b
4: The f → mb is a functor from type x to monadic value mb where b

The Kleisli compose operator >=> means if some m >>= f then f is the continuation of m.[1] -- Ancheta Wis   (talk | contribs) 18:59, 27 October 2022 (UTC), 01:42, 28 October 2022 (UTC)[reply]

Alexis King[2] notes that if m >>= f >>= g >>= h then the continuation of m is f >=> g >=> h [3] --Ancheta Wis   (talk | contribs) 19:18, 27 October 2022 (UTC)[reply]

The notation doesn't make much sense and seems to have been introduced quite randomly and without references in https://en.wikipedia.org/w/index.php?title=Monad_(functional_programming)&diff=next&oldid=867467071 , along with a few other strange changes by user:Zar2gar1 in the vicinity. To me, user:Nbrader's edit is a correction. --Daniel5Ko (talk) 23:27, 28 October 2022 (UTC)[reply]
So which is it,
(f >=> g) x = (f x) >>= g
or some other parenthesization needed?
(f >=> g) x = f x >>= g
ping user:Zar2gar1 --Ancheta Wis   (talk | contribs) 00:14, 29 October 2022 (UTC)[reply]

@user:Zar2gar1, Thank you for your pseudocode in the article, which attempts to accomodate the requests of the readership to avoid Haskell. Because of the objections above, I propose the use of Bartosz Milewski's 2019 book Category Theory for Programmers pages 202 through 205, which uses Haskell notation to explain Kleisli composition >=> . Dr. Milewski uses Kleisli composition >=> and return to implement Monad. In fact Dr. Milewski implements Monad in Haskell three ways on those pages. --Ancheta Wis   (talk | contribs) 01:08, 29 October 2022 (UTC)[reply]

@"which is it": The two alternatives have the same meaning. Even more: They already have identical parse trees. --Daniel5Ko (talk) 00:40, 30 October 2022 (UTC)[reply]
Firstly, sorry for replacing the pseudo-code with Haskell code without reading the talk page first: I didn't realize there was an effort to avoid Haskell code. I will say though I found the pseudo-code confusing. My feeling (for what it's worth) is that psuedo-code that has no clear meaning to most people defeats the point of pseudo-code and you're better off going with an actual language like Haskell. Nbrader (talk) 01:21, 30 October 2022 (UTC)[reply]

The equation "(f >=> g) x = (f(x) → mb) >>= g(y = b)" is very confusing, it's unclear where mb and y are bound; I have never seen anything similar. The explanation in this thread is also misguided: x is not a type and there is no substitution happening that forms functors.

It makes sense to avoid Haskell-specific notation, but here the only notation used is function application written as "f x", and binary operator "f >=> g". I restored the equation, using f(x) as function application. That should be understandable without additional prerequisites. 2001:861:3F42:1B60:AEFC:B99:1842:A29E (talk) — Preceding undated comment added 07:57, 24 November 2022 (UTC)[reply]

Hi everyone, I'm still going to be away from Wikipedia for the foreseeable future, but I did get the ping for this conversation. I'm going to try to keep my response simple. For the specific expression about monadic composition, just like you guessed @Ancheta Wis, the "g(y=b)" expression is a kludge to show variable binding and application in the few cases leaving them implicit is ambiguous. It's not the cleanest way for sure, but it does allow avoiding lambda calculus as a pre-req to understand the article.
Big picture, anyone that wants to think through & change the code has my full support. I left a lot of comments and notes in the Talk archives while finishing up my changes though. My main opinions are pretty much:
  • Avoid making mostly independent concepts like lambda calculus or currying pre-reqs to understand the article
  • Try to be consistent and head-off misinterpretations around things like order of evaluation
  • The "right thing to do" would probably be to hash out functional pseudo-code guidelines for Wikipedia at large
  • Barring that, I do feel math-ish notation is preferable, followed by something like an established functional language (just looser & more accessible)
I don't expect people to read my notes (they're long), but if you are willing to, you may still want to factor in my rationale at some points. Zar2gar1 (talk) 05:49, 28 November 2022 (UTC)[reply]

Monad versus monoid[edit]

Talk for BRD -- Ancheta Wis   (talk | contribs) 02:03, 7 November 2023 (UTC)[reply]

Permutation?[edit]

It was added at https://en.wikipedia.org/w/index.php?diff=438062307 that the monoid being "non-commutative and idempotent" implies permutation. I think the property as described cannot mean "free and idempotent", as [a, b, a] wouldn't be called a permutation. Is there a way to clarify the property to really give permutations? Otherwise, is it even meaningful to name the collection type arising from free and idempotent monoid? Junghyeon Park (talk) 04:27, 11 December 2023 (UTC)[reply]

Rust example[edit]

Hi all. I would like to ask regarding the Rust example. It is written in Monad (functional programming)#An example: Maybe that the Rust example is using Maybe, Just, and Nothing, while Rust actually uses Option, Some, and None respectively [1]. Is this intended to be written this way? Mangkoran (talk) 11:19, 19 December 2023 (UTC)[reply]

It is not claiming to be in std namespace. While it is probably influenced by the crate https://crates.io/crates/rsmonad, I'm not sure if it's worth mentioning since the API seems pretty generic. Would some clarification help maybe? Junghyeon Park (talk) 07:36, 19 January 2024 (UTC)[reply]