1. 23
  1.  

  2. 11

    What’s different about computer science, at least for me, is that it’s accessible. One of the most complicated topics in computer science is operating system design…but I can sit at a computer and just piddle around and get an intuitive idea of “process” and “file” and what-have-you. I can do this with no prior foreknowledge beyond how to type and read and basic “computer literacy” skills.

    Pick a similarly complicated topic in mathematics, say, topology. I can’t sit down with basic “math literacy” and just piddle around and get an intuitive feeling for topology. Maybe there’s a way to do that, but I don’t know how.

    1. 15

      I used to feel this way, until very recently. What changed for me was that I realized I should be reading a math book with a notebook next to me. I know this sounds stupid or obvious, but it wasn’t to me. So when I read a definition, I rewrite it in the notebook and I try to really understand it. The very next thing is usually a very simple proof using that definition. I read the statement of the proof, and then I try to write the proof without looking at what’s in the book. Reading proofs is still a new skill for me, so they’re fairly opaque looking anyway, and it’s not hard for me to ignore it while I make my attempt. After five or ten minutes of struggling, I’ll read the proof in the book and by then, it’s like the struggling has made me really think about what’s going on, and I usually am in the right frame of mind to understand it. This also takes some time. I’ll wind up copying the proof from the book into my notebook, but in my own words or (occasionally) other notation.

      This isn’t that different from what you do with computer science by reading with a computer nearby. In both cases, you don’t really expect to understand what you’re reading on your own, without doing some experimentation.

      I have a Dover book on topology, Mendelsohn’s Introduction to Topology, and I have been trying to work through it for a long time. I’m in a class on abstract math now, and I’m hoping that I’ll make more progress in the topology book after I get through the class. But I was actually starting to get somewhere with it following this approach.

      1. 1

        I would very much appreciate some book recommendations if you’ve got the time.

        1. 6

          Introduction to Graph Theory notoriously espouses the ideas outlined here: https://www.goodreads.com/book/show/388049.Introduction_to_Graph_Theory

          1. 1

            Thank you, added to the list.

          2. 3

            Sure! On what? The text for my class is Introduction to Abstract Mathematics but the other book I mentioned is Introduction to Topology.

            1. 1

              Thank you!

            2. 2

              Pinter, “A Book of Abstract Algebra” is great for self-study. Each chapter is a short presentation of some basic definitions, followed by a series of exercises designed to guide you through discovering important facts for yourself. I highly recommend it!

              1. 1

                And it’s on the list too, thank you.

          3. 10

            I can’t sit down with basic “math literacy” and just piddle around and get an intuitive feeling for topology. Maybe there’s a way to do that, but I don’t know how.

            You can. It’s not called “math literacy”, it’s called mathematical maturity. It takes a lot of practice – the equivalent of a good chunk of an undergrad degree to develop it. It’s a fuzzy concept (literacy is more binary), but it’s when you become more self-sufficient. You come up with your own examples to probe different concepts, you don’t need someone to tell you if a proof or line of thinking is correct, you start to see the broader consequences of why a result is true and what it means outside of how it was defined, you start to see how different results are connected and how while a book or a paper took one path to a result there are many paths that could have been followed, etc.

            operating system design…but I can sit at a computer and just piddle around and get an intuitive idea of “process” and “file” and what-have-you. I can do this with no prior foreknowledge beyond how to type and read and basic “computer literacy” skills.

            This is because a lot of problems in computer science and still pre-theoretical. We muddle our way through what a good operating system should look like because we don’t have a theory that lets us formally define what “good” is and then reason about “goodness”. It’s the same in machine learning. We have theory to talk about how to optimize a function, but we don’t have theory to talk about what object detection really is as a problem besides “match the labels people give to objects”.

            Eventually, this will change. Theory in physics took a long time to mature. The ancients could ask questions about atoms, gravity, magnetism etc. just fine. And they came up with plenty of reasonable hypotheses, some of which they even tested. That’s where we are in computer science today. Fixing the situation and finding theory where these big questions can be asked precisely and where they can be given answers took 1000+ years. Hopefully computer science will move much faster.

            1. 9

              As a software engineer, I agree that software feels more accessible. But most us of were never taught mathematics in school. And by most I am suggesting a very significant majority. It would be terribly difficult for math to feel accessible if that’s true. In Paul Lockhart’s referenced diatribe (https://www.maa.org/external_archive/devlin/LockhartsLament.pdf) against the way math is taught, he makes a very strong case for this.

              1. 3

                In my first year of CS, I wrote some code that took a lot of samples from a uniform random distribution, bucketed them, and made a bar-plot of them (effectively plotting something like the probability distribution function – although I didn’t know this concept at the time). The result was as expected.

                Then, I replaced the random sample by the sum of two random samples. Suddenly the box-plot started looking more like a tent function. I didn’t understand why, but this seemed interesting! I tried the product of two random samples as well, which gave a similar interesting result.

                I was a little bit annoyed that I didn’t immediately understand something so simple. I understood how random numbers work, and how sums work, so certainly I should be able to deduce how the sum of two random variables behaves?

                I then sat at my desk for a long time and, by trying to understand the various graphs my code produced, gained a good intuition for pdfs, cdfs, and the concept of convolution. I just loved being able to have a problem, and just solve it by virtue of thinking hard enough about it.

                I started a maths degree after that year.

                Edit: On the other hand, I feel like in computer science it is much more common that you just have to know how certain things work under the hood, while there is zero indication of how you possibly can find out. I’ve had this experience when I used Arch Linux and NixOS a lot. It’s also common in C++. If you take a pointer to an element in a vector, it’s fine. If you do it but then add an element to the vector, your pointer might or might not point to accessible memory.

              2. 11

                Adriano, I feel this article is a bit of a “no true scotsman” on math. I think you’re setting the bar a little too high for what is math: you seem to be saying, unless it involves the mathematical mindset, and it solves a novel, practical problem that doesn’t have a pre-canned solution, it isn’t math. You didn’t do math in school despite taking math classes and writing proofs, so there’s no math in those things. Despite all this, you did manage to do math one time. It comes across as strangely humble-braggy. You hate the stupid notation, then go out of your way to show it to us—twice.

                I agree that what’s essential is the mathematical mindset. I agree that it’s easy (or at least not impossible) to pass many math classes without getting into the mathematical mindset. But that’s true in any subject—passing classes is a skill in its own right. Where I disagree is that I do think that doing problems that have already been solved teaches the mindset. I think doing proofs helps you think mathematically. On the other hand, I definitely agree that what happened to you in “Business Math” is more like calculation or arithmetic—but that is also a skill people need to have, and you need some facility with it to get to the more interesting stuff.

                For the class I’m taking now, which is called “basic concepts of mathematics” but is probably more properly called “introduction to abstract math,” the professor assigned this excellent article entitled “What Mathematicians Do” by AJ Berrick. What I like about this article is that it talks about the mindset exactly. I bet if you read it, you would find something to agree with in it.

                1. 4

                  I think you’re setting the bar a little too high for what is math: you seem to be saying, unless it involves the mathematical mindset, and it solves a novel, practical problem that doesn’t have a pre-canned solution, it isn’t math.

                  This is a general school problem. It is often observed that most “science fair experiments” are actually “science fair demonstrations” because you already know that e.g. baking soda and vinegar make lava. It’s pretty rare to do actual “we don’t know the answer to this interesting question” science in school. Similarly, if you’re into a humanities course in college, you generally just learn about work other people have done in the field—you read the famous philosophers, recent histories, English literature, etc.—but for the most part it isn’t until graduate school that you’re expected to write your own novel history based on your own research, have your own theories of philosophy in dialogue with the current journals, write your own novel at the Iowa Writers Workshop…

                  There are exceptions, and you tend to do more of the real thing as you go up the ranks, but for almost all school subjects to a large degree until grad school you are mostly learning about what other people did on the topic and not doing the thing yourself.

                  1. 3

                    Some of this is because the low-hanging fruit has already been picked, and much of it turns out to be more difficult than it would seem at first blush. Similar questions can have wildly different difficult scales; it’s pretty easy to prove that there are infinite numbers, easy but somewhat less so to prove there are infinite primes, but incredibly hard to prove whether there are an infinite number of prime pairs or Mersenne primes or whatever. It’s easy to prove that pi is irrational, or e is irrational, but it’s still unproven whether pi + e is irrational.

                    It’s pretty easy for you to show a kid an experiment like vinegar and baking soda that is interesting, but then they just go through the kitchen mixing everything together, and nothing interesting seems to happen. They’re willing to do the work. But most results are null results, which aren’t very interesting. They’re also happy to go through the cleaning supplies and mix all of them together, which could put them in danger. You want them to be successful when they have the wherewithal to keep trying, but you also want them not to get hurt or discouraged. So we tend to put them in sandboxes where they can’t get hurt, but also don’t have to struggle so much. Learning is not the top priority—at scale it just can’t be, it has to come after safety.

                  2. 3

                    You make a fair critique, and I can hardly do justice to Paul Lockhart’s 25 pages on the topic, so it’s no surprise that I’ve failed in delivering my point to some readers. The purpose of showing “the math” twice as you pointed out is that those example are what a lot of people think about when they think of math. And for me personally, that’s the sort of thing I’ve always thought of when I thought of math, and I posit that it’s not. It’s the pure vs. applied mathematics argument. Sure, applied mathematics should be considered math, but it’s not the soul of it.

                    Like Lockhart says (paraphrased), math isn’t knowing 1/2 * bh is the area of a triangle; it’s knowing that when you place a triangle in a box and place an imaginary line inside the triangle, you may come to the visual realization that the triangle must occupy half the box.

                    1. 1

                      Lockhart’s article is pretty famous but I haven’t found the time to read it, I’ll make a point of reading it now. Thank you for taking the time to write.

                  3. 9

                    Oh man. I sometimes think back to the days in university when I was solving calculus and algebra. Mind you, I was average in class, my grades where OK, but not great. But. My brain was a machine back then! I could integrate stuff in several steps in my head at the same time holding three or four other things in my mind at the same time.

                    I often marvel at how increasingly stupid I have become over the course of my 15 year career and I often think about how incredible my mental capabilities where in my early twenties. I don’t know if it is my aging brain that’s the difference, or if it is just the fact that I was solving math problems every day for five years, back then.

                    Anyway. Sorry if it was not relevant. Just something I’ve kept in for too long. I should dig up my old calculus books and do some problems recreationally and nostalgically. Math is awesome.

                    1. 7

                      I often marvel at how increasingly stupid I have become over the course of my 15 year career and I often think about how incredible my mental capabilities where in my early twenties. I don’t know if it is my aging brain that’s the difference, or if it is just the fact that I was solving math problems every day for five years, back then.

                      In my experience, it’s really the latter :-). I’m in a pretty similar situation. Luck and some programming knowledge made me the programming monkey in an otherwise serious research team back when I was in my 3rd year of uni – I could comfortably do triple integrals in my head, whereas now my brain hurts when I run into simple first year math. Meanwhile, some of my former colleagues are still comfortable doing all sorts of complicated math and they’re entering their fourties. I’m a few years behind them and integration by parts makes my heart race now. But they did spend the last fifteen years doing hard math, and I didn’t.

                      The good news is that most of it seems to come back fairly quickly, after surprisingly little practice. A few years ago I had a brief re-encounter with some pretty tough DSP problems (tl;dr real-time data acquisition and noise cancellation), which was double tough because, while I was extremely interested in signal processing, I never had time to study it as much as I wanted. After two days or so of how the fsck did I fit all this into my brain and I hope Fourier died a slow and painful death, I was surprisingly comfortable. I did have to peek at a handbook a little more often than I would’ve had to ten years before, but mostly because I was paranoid about not remembering the formulae and I kept double-checking them.

                    2. 7

                      I vividly remember writing out a physics problem in an exam, the sort where they give you an Interesting Problem that you should be able to figure out but have never actually seen before. I toyed around with the problem, then realized that it was in the form of an infinite series that I recognized and knew how to get the solution to. That’s the first time I remember thinking “Wait a second, the stuff they’re teaching me in math classes is actually useful. This is actually a tool that is worth knowing how to use.”. Would be nice if it had happened before midterm exams in my last semester of undergrad…

                      I did the first proof that I remember a few years later; it was just proving that a number could never be 0 or less and so I didn’t need to worry about it in an orbit simulation. I was just writing down logic of it in code comments for the function in question and went “oh wait, I just proved something”.

                      1. 4

                        Because graduating with an undergraduate computer science degree doesn’t require mathematics at all. At least not so much that you can’t temporarily memorize some general mathematical techniques for an exam and forget it all the next day. They won’t be used outside of an exam setting; trust me.

                        I know every university is different, but for me I’ve had to take Calc 1, Calc 2, Discrete Math, Linear Algebra, & Set Theory. I don’t know of any friends at other schools who are doing CS and don’t have to do the “real math” mentioned in the article. I’ve never really understood the mindset of “CS doesn’t need Math” which, while true for Web Programming, doesn’t hold true once you reach advanced CS fields. Most of the Unsupervised Machine Learning course I’m taking now is understanding the linalg. Advanced CS definitely requires advanced math. That intersection is where all the fun CS topics are!

                        I’m looking at you proofs. But even proofs aren’t really mathematics. Students do proofs because instructors ask them to do proofs; they’re not actually proving anything novel.

                        This is an interesting take. Immediately I think “how can you prove novel things if you don’t intuitively understand previously proven things?” There is a part of me that believes if Software Engineers were forced to write F* we’d have fewer planes falling out of the sky.

                        Tangentially, the pictures shown in the article look like they’re straight out of my Algorithms textbook (notably not a math course but a CS course). While exploring various new avenues in math, specifically Homotopy Type Theory & Higher Category Theory, I think I’ve formed a better picture of what “math” really is. I wish the syntax was as easy to read as those pictures. Sadly mathematicians don’t write their math in pseudocode.

                        1. 2

                          I’ve attended advanced CS courses that required a ton of math. E.g.:

                          • Program analysis: Required Galois connections and other abstract algebra topics
                          • Computationally hard problems: Required good background in logic and discrete math
                          • Data logic: Required high order logic and term rewriting background
                          • Real-time systems: Required automata theory
                          • Numerical methods: Required real analysis and linear algebra
                          • NLP: Required a bit of type theory for understanding Montague grammars
                        2. [Comment from banned user removed]

                          1. 4

                            I sympathize. I felt this way for a long, long time. Eventually I did learn that math can be a sort of art all of its own, and that mixed media art can be pretty satisfying sometimes. If you turn the other direction and view programming as building things, math is also a useful tool to have in your toolbox. I highly recommend you read A Mathematician’s Lament sometime, or at least the first part of it.

                            That said, I still don’t actually enjoy doing math very much. But I can bend it to my whims from time to time if it’s useful.

                            1. 4

                              To answer the factual part of your post, surely painters need math. See https://en.wikipedia.org/wiki/Mathematics_and_art#Perspective_and_proportion for an example.

                              To generalize my disagreement, mathematics is a language for various abstract concepts. If you use an abstract concept in your work, chances are, math has some name for it, and possibly also an interesting—maybe even useful—set of conjectures about it.

                              Now, for a personal anecdote. At high school, the only math I did was… the usual math problems. Integrate this, take a derivative of that, prove some geometrical conjecture. I felt contempt for math: who would want to spend their time looking at some symbolic expressions and moving parentheses about? Fast-forward a few years, and at some point in my undergrad, I’ve read Alexandrescu’s “Modern C++ Design”, a deeply mathematical book that cleverly hides it, as I’ve understood later. In this book, the author builds a set of useful snippets with C++ templates that any sensible language ought to have built-in. Inspired by this, I set out to invent my own language, but, frustratingly, the results were very disappointing: my language was huge, bloated, inelegant. Then, one professor of mine mentioned Haskell. After a quick look at it, I canned all of my developments: this was the language that I was trying to invent. So, the answer to the task of language design from the perspective of a C++ programmer who felt contempt for math was actually a math-inspired language.

                              Turns out, math really does underlie programming. It’s a huge shame that programmers don’t know this—it’s also a shame that mathematicians don’t care to understand programming deeply enough to be able to apply their knowledge—or there would be much less stumbling about in our industry. Mathematical logic lies at the heart of the process of debugging. Abstract algebra is actually the science of writing extensible, simple, intuitive abstractions—which is what programmers do all day. Category theory is overrated as a computer science discipline, but it also has its uses for a software architect trying to make sense of a particularly sprawling set of abstractions. Real analysis, which is what math is for most people… well, this one is quite far from day-to-day programming, I’ll give you that.

                              1. 1

                                Okay, then what the hell is a monad that someone not versed in category theory (or really, any form of “mathematics”) can understand? The stock answer of “a monad is just a monoid in the category of endofunctors” is not an acceptable answer. Or is it just jargon that Haskellers came up with as a form of “gate keeping” function? (I recall there being a few “gate keeping” classes in university that just served to winnow out students)

                                1. 5

                                  The stock answer of “a monad is just a monoid in the category of endofunctors” is not an acceptable answer. Or is it just jargon that Haskellers came up with as a form of “gate keeping” function?

                                  It’s a joke:

                                  1990 - A committee formed by Simon Peyton-Jones, Paul Hudak, Philip Wadler, Ashton Kutcher, and People for the Ethical Treatment of Animals creates Haskell, a pure, non-strict, functional language. Haskell gets some resistance due to the complexity of using monads to control side effects. Wadler tries to appease critics by explaining that “a monad is a monoid in the category of endofunctors, what’s the problem?”

                                  1. 2

                                    you don’t need to understand category theory to understand haskell’s monad structure, it is based on the category theory one but it is much simpler.

                                    the first thing you need to understand and get used to is the static typing system and creating simple data types with ‘data’. Once you have got used to that look at Functor/fmap to start with.

                                    1. 2

                                      Thanks for this comment. I can’t help but think that certain Haskell proponents become hopelessly nerd-sniped when asked to explain monads, instead of taking the pragmatic approach of saying you don’t really need the theory from day one to effectively use the language.

                                      In fact, the “trying to explain monads” has become its own meta-meme, complete with new entrants, gentle (or not so gentle) ribbing from people who know perfectly well what it is but still want to see people get lost in the weeds, and perhaps a perverse celebration of Haskell’s “outsider” status.

                                    2. 2

                                      As someone with a math degree I sympathize with how you feel about gatekeeping. In high school, I was interested in math, but the “math club” at my school took all the fun out of math by turning it into a competition. In college the “honors math” track was full of the same type of people, but I learned to just ignore the loud jerks and surround myself with some more supportive people. So I’d encourage you not to let people keep you from exploring something you might be interested in!

                                      Quite a lot of ink has been spilled on the subject of monads (see here and here), so I’m sure you can find an explanation that suits your background. Pragmatically, a monad is useful to programmers as a control flow structure that allows one to 1) define some kind of contextual operation, such as saving or writing to a variable, or a collection of variables and 2) explicitly define how to sequence those contextual operations, i.e. how those contextual operations behave when performed one after the other. In short, monads are the only place in your code where you’re allowed to use side-effects, which many people see as an advantage to using a language like Haskell.

                                      One thing that helped me was to see how Haskell’s do-notation desugars:

                                      f :: Int -> [Int]
                                      f x = [0, x, 2*x]
                                      
                                      g :: Int -> [Int]
                                      g x = [x, -x]
                                      
                                      -- do notation
                                      h1 :: Int -> [Int]
                                      h1 x = do
                                        y1 <- f x
                                        y2 <- g y1
                                        return y2
                                      
                                      -- desugared
                                      h2 :: Int -> [Int]
                                      h2 x = f x >>= 
                                             (\y1 -> (g y1 >>=
                                             (\y2 -> return y2)))
                                      
                                      main :: IO ()
                                      main = do
                                          print $ h1 5
                                          print $ h2 5
                                      

                                      From the signature >>= :: Monad m => m a -> (a -> m b) -> m b, we can tell that >>= for the monad m explains how to apply a function a -> m b in the context of a monad m a. In the simplest case, for the identity monad, >>= is literally function application. In my example, the List monad, >>= happens in the context of an array of integers, and >>= :: [a] -> (a -> [b]) -> [b] is like a flatMap operation.

                                      1. 4

                                        Honestly, your explanation is as clear as mud to me. I keep thinking back to this Feynman story:

                                        Feynman was a truly great teacher. He prided himself on being able to devise ways to explain even the most profound ideas to beginning students. Once, I said to him, “Dick, explain to me, so that I can understand it, why spin one-half particles obey Fermi-Dirac statistics.” Sizing up his audience perfectly, Feynman said, “I’ll prepare a freshman lecture on it.” But he came back a few days later to say, “I couldn’t do it. I couldn’t reduce it to the freshman level. That means we don’t really understand it.”

                                        You also assume a knowledge of Haskell in your explanation, which I don’t have. It’s why I’m asking.

                                        1. 3

                                          A monad is an applicative with some extra stuff. An applicative is a functor with some extra stuff. A functor is a kind of complex type which wraps a single value, along with a way to transform functions on primitives to functions in the wrapped type (where the transformation “behaves nicely”). list counts as a functor because map transforms a function on individual values of the list to a function that maps over the entire list.

                                          Applicatives are that plus the ability to wrap store functions in the same data type and use them, like “take this list of functions and apply each of them to this list of values.” This opens up various things, particularly binary operators: you can write a function that takes two values and automatically transform it into one that takes two applicatives.

                                          Monads are that plus the ability to take functions that take primitives and output wrapped values and apply them directly to wrapped values. Explaining why this is useful is a bit harder, but it’s a lot easier to grok if you already know functirs and applicatives. We only introduce monads first because they were formalized earlier.

                                          1. 1

                                            “Container of functions” is one way to understand Applicative, but I think a more useful one is “generalizing fmap to multi-argument functions” (of course those don’t really exist in Haskell, but you know what I mean). Anyway, I think this perspective better describes how Applicative is actually used.

                                          2. 1

                                            To be fair I didn’t intend it to be a fully self contained explanation. Just a taste of one perspective you might encounter if you click through some of the more complete tutorials out there. I think of Haskell as “theoretical assembly language” in the sense that it’s as close to pure lambda calculus as we can reasonably get while still remaining practical. Just like it might take a Python user a while to grasp assembly language, Haskell takes some time to get comfortable with if you’ve never thought that way before. I’d say learning Haskell basics is about the quickest path to understanding what a monad is, since most monad tutorials . You might have to think about “computation” differently than you might be used to. I’m not saying it’s easy to understand, but I think there is a path to understanding without doing a whole math degree first.

                                            1. 2

                                              And I’ve yet to see a decent monad explanation that doesn’t require a whole math degree first to understand.

                                        2. 1

                                          I’ve asked a few folks about this; I’m not sure if there’s an easier way to explain monads, although I’m open to hearing it. In particular, the path which talks about adjoint functors is probably not easier. nLab’s version describes a monoid in the category of endofunctors in alternative words, using “associative in the evident sense and unital with respect to a given unit function” for monoid and “sends every type X … to a new type T(X)” for endofunctor. WP has two pages; one page describes monads using adjoint functors, and the other page is a functional-programming monad tutorial.

                                          Monads predate Haskell by about four decades. I have seen folks with only public school mathematics education consider individual instances of adjunctions before, although they did not imagine that anybody might systematically investigate such things. I’m therefore tempted to not take this second part very seriously.

                                          1. 1

                                            Okay, then what the hell is a monad

                                            You seem to be… in disagreement? I’m not sure with what. I don’t claim that things taught to programmers by math are trivial, if this is the impression that you got; otherwise, why would anyone advocate learning math if the same insights could be gained more easily? I would have great trouble explaining recursion schemes or the concept of a fixed point to someone who doesn’t know math, for example.

                                            Luckily, monads are really a simple concept. I routinely explain them in real life to people with programming experience (I teach functional programming), usually with success.

                                            We can call a data structure T a monad if it supports three operations: template <class X> T<X> join(T<T<X>>), template <class X, Y> T<Y> map(Y f(X), T<X>), and template <class X> T<X> pure(X). For example, arrays are monads: having [[int]], we can get [int] by concatenating everything; having a function that transforms an int to a char, we can transform [int] to [char], and we can put a value into a single-element array. Also, asynchronous futures are monads: if one has Future<Future<String>>, one can get a Future<String>, awaiting on which first awaits the outer future, then awaits the resulting future; the other operations are also simple. There are some restrictions on these operations, called monad laws, so not every implementation of these operations is a monad.

                                            An alternative definition replaces both map and join with template <class X, Y> T<Y> bind(T<X>, T<Y> f(T<X>)).

                                            Now, why are monads important? The join operation looks fairly useless. And yes, outside of Haskell, monads lose their appeal somewhat. The reason haskellers are enamored with monads is that, in Haskell, it’s comically easy to write interpreters of small programming languages, so in the course of a typical day, a haskeller may write an interpreter just to solve a small problem. And monads are very useful for writing interpreters. Below I’ll try to explain why.

                                            C has the ; operator, which takes two pieces of a function and creates a new one. For example, consider this silly program:

                                            #include <stdio.h>
                                            #define B(S1, S2) {S1;S2;}
                                            
                                            int main(int argc, char *argv[])
                                            {
                                                B(B(int x = argc, if (x == 0) { B(printf("error\n"), return 1) } else { }), return 0);
                                            }
                                            

                                            Now, what does B actually do?

                                            1. “It takes a piece to the left, executes it. Then it takes a piece to the right, executes it” would be wrong: if a return happened in the left piece, then the right piece won’t execute.
                                            2. “It takes a piece to the left, executes it. If the left side finished normally (without doing break, or return, or continue, or anything), then the right side is executed” would be more correct, but could be improved further. For example, what about variables? The right side is not run in isolation, after all.
                                            3. “It takes a piece to the left, executes it. If the left side finished normally (without doing break, or return, or continue, or anything), then the right side is executed, with the knowledge of the variables declared in the outmost scope of the left piece and with the initial values of all visible variables corresponding to the values set in the left piece or, if the left piece left them unchanged, the values initially given to the whole B(...)”. Now, that’s a mouthful, and still far from the truth.

                                            When you’re implementing interpreters and need to implement a ;, the way to do it is by defining a monad whose bind is the equivalent of a semicolon. The details of this equivalence are difficult to express in C, but I hope that I explained the general idea of what a monad is and why we hear so much about them.

                                            1. 1

                                              This is honestly the first time I’ve seen the ‘;’ (in the context of C) as being an operator. And your explanation of the ‘;’ operator (all three tries) fail because it’s not an operator. Or it’s an operator in the sense that the ASCII character LF is an operator in Python (in my opinion, it’s not an operator in any meaningful sense of the word).

                                              Also, your explanation using C++ is lost on me since I don’t use C++ (C, yes. Assembly, yes. C++, not so much).

                                              1. 1

                                                And your explanation of the ‘;’ operator (all three tries) fail because it’s not an operator

                                                The C standard doesn’t define ; as an operator; however, it just as well could have. The problem is that C has several syntactic categories: statements, expressions, declarations… and only defines operators to be in the context of expressions. See, in Haskell and other functional languages, there are no statements, only expressions, so in the programming language community, it’s not uncommon to see ; being called a sequencing operator, operating on statements. I guess I can see why my explanation confused you: different cultures.

                                                In any case, you can imagine ; being an operator that accepts two statements of a program and returns a compound statement, can’t you?

                                                in my opinion, it’s not an operator in any meaningful sense of the word

                                                What a culture shock it would be for you then to learn Haskell! It uses a space as an operator; the function-calling operator, to be exact.

                                                Also, your explanation using C++ is lost on me

                                                A shame, as C doesn’t provide a way to define monads without drowning in macros. So, template <class X> T<X> join(T<T<X>>) means that there is a definition of join for every possible T and X, and X<Y> means “X specialized in some way to Y”. In C some join instances could be written as

                                                int_list join(int_list_list);
                                                char_list join(char_list_list);
                                                

                                                where

                                                typedef struct {
                                                  int_list *next;
                                                  int value;
                                                } int_list;
                                                

                                                and

                                                typedef struct {
                                                  int_list_list *next;
                                                  int_list *value;
                                                } int_list_list;
                                                

                                                Hope I didn’t mess up the syntax.

                                                1. 2

                                                  See, in Haskell and other functional languages, there are no statements, only expressions, so in the programming language community, it’s not uncommon to see ; being called a sequencing operator, operating on statements.

                                                  But I thought that order of expression evaluation wasn’t defined in Haskell (except via monads) due to its functional nature. So how can ‘;’ be considered a sequencing operator and not just an expression separator?

                                                  you can imagine ; being an operator that accepts two statements of a program and returns a compound statement, can’t you?

                                                  Yes, but not as something that actually runs the statements as you tried to state.

                                                  Also, I’m still unclear what join does. You mention it, but you don’t actually state what it does. Your somewhat mangled C code doesn’t even make it clear what it’s supposed to do.

                                            2. 1

                                              I wouldn’t try to learn an advanced mathematical construct like monads in terms of metaphors. Metaphor has its place in the “post-rigorous” understanding of a concept (Terry Tao’s phrase), but I think it tends to be an impediment to learning. Instead, I would recommend finding as many motivating examples as you can for a construct, and try to understand how each example motivates the operations and laws of the mathematical construct you’re trying to learn. You have to be careful to diversify your examples, though, or you might end up thinking that e.g. Functor is “just a container”, while there are many functors that look nothing like containers. But as a way to get intuition for a concept, I think finding the most diverse set of motivating examples you can, and applying the concept to each example in detail, is the most effective approach, at least for me.

                                              1. 1

                                                Looking at it from a very simple and practical perspective, a monad is something that has an “and then” method. Here are a few examples:

                                                • A Promise in JavaScript. A Promise will eventually return some value of type A. If you want to then run another asynchronous computation on it, you can call then, which accepts a function as an argument; the function that you feed to then will be given a value of A, and return another Promise that will eventually resolve to a value of type B. The result of calling then is a Promise of type B.
                                                • A nullable / optional value. Consider the C++ std::optional<T>. Suppose you have an optional<Person>, a value that may or may not contain a person, and the person may or may not have a middle name (their middleName field would be an optional<string>. To try get the middle name, you need to check if the optional value contains anything, and if it does, return the middle name field. More generally, you can write a then function that unwraps an optional value, applies some other function to the result if it exists, and returns an optional with that result.
                                                • A list (though this is getting a little bit less intuitive). Consider a list of type A and the function flatMap that converts each element A into a list of some type B, and combines the resulting lists. The result is a list of type B.

                                                Here are the common elements between these three examples.

                                                • In each case, our “monad” is a “container” that wraps another value. A promise “contains” a value in the sense that it will eventually return it. An optional value may or may not contain something (like a Person). A list contains zero or more of some kind of value.
                                                • In each case, there is some kind of function (“then”) that accepts another function as argument. This argument function is given an “unwrapped” value from the container, and produces another “container” with some other value. The result of “then” is a container of that kind of value.

                                                In very simple Haskell notation, we write:

                                                • m a for some “container” m that contains values of type “a”.
                                                • a -> m a for a function that takes an unwrapped value a and produces a container m a of this kind of value.
                                                • a -> m b for a function that takes an unwrapped value a and produced a container m b of some different kind of value. For example, this function could take a Person and return an optional<string>.
                                                • m a -> (a -> m b) -> m b for the type of the “then” function, which accepts a container with some value a, a function that converts an unwrapped value a into another container with b, and finally returns a container of b.

                                                And that’s the gist of what a monad is. It’s very abstract, which is what makes it initially hard to grasp - why “and then”? Why that particular type for the function? It just so happens that this kind of function arises a lot in programming, under many guises. Once you’re comfortable with it, you start seeing it everywhere.

                                            3. 2

                                              I can’t tell if you’re being sarcastic, or trolling.

                                              1. 1

                                                Honestly not sure if this is sarcasm? Programming is algorithms and data structures, which are both branches of mathematics.

                                                OK, there’s not much of that in building a website, but basic web dev is to programming as arithmetic is to mathematics.

                                              2. 2

                                                I’m looking at you, proofs. But even proofs aren’t really mathematics. Students do proofs because instructors ask them to do proofs; they’re not actually proving anything novel.

                                                That so many students leave high school with this attitude about math is a failure of our education system. A proof is nothing more than a thorough explanation for why something is true. In math, unlike many other fields, we have the luxury of being able to decide with absolute certainty that certain facts are true. Logical fluency is one of the main benefits of a math education, with advantages reaching far beyond the classroom. The more math I learn, the more easily I’m able to jump into unfamiliar topics.

                                                I would even venture to say that American colleges should get rid of the usual Calc 1/2/3 sequence, which is more about rote memorization than understanding, and have everyone take proof-based real analysis and linear algebra courses instead (with some applications sprinkled in, to keep things grounded in reality – there are tons of easy examples to draw from in physics / computer graphics / engineering / etc).