7 ms·
Wait, a functor is just a pure unary function? And an endofunctor is just one of those where the argument type and return type are the same? This could have bee
by brundolf 5y ago
Wait, a functor is just a pure unary function? And an endofunctor is just one of those where the argument type and return type are the same? This could have been explained to me years ago, in five minutes?
Sometimes it makes me mad that so much confusion (and as another commenter put it, gatekeeping) has been sown in FP circles through the invention of pointlessly-obfuscated terminology for everything (and through - among many in those circles - a lack of interest in connecting the very simple dots that would allow masses of people to understand these concepts). It's like when the medieval Catholic Church refused to print a bible in anything but Latin so that they'd remain the sole authority on its content.
Kudos to the OP for cutting through some of that, but shame on those who maintain this status quo.
Edit: assuming it really is (roughly) that simple. Some in the comments have suggested parts of the OP were wrong (assuming they aren't just being pedantic), so I'm curious to see discussion.
- tomc1985 5y agoKind of reminds me of an old saying I heard somewhere, that I'll paraphrase here: "novice writers use little words, intermediate writers use big words, and expert writers use little words." Like, it's fun to communicate in technical jargon -- and personally I'm quite proud of the vocabulary I have built up over time -- but you are totally correct in that it serves as a gatekeeper for an in-group to retain its dominance over a given field. There is a lot to be said for effective communication using a simple, base language and reserving obtuse technical terms for areas where one needs to be very specific. There's another point to be made for not inventing new technical terms when existing ones apply. The amount of overlapping terminology in this field is infuriating to me. This is true across all disciplines...
- Littlebigo 5y agoI very much agree with part of your point, especially the novice-intermediate-expert part on jargon. I generally favor wording that is as simple as possible, but no simpler. Sometimes technical precision is important, and when talking to colleagues and other people that are on a similar technical proficiency level as me I prefer concise and informative words. I'd rather say "ARP poisoning" than "network attack", because the latter loses information, but would obviously use the latter to muggles. I don't agree that having a domain jargon that is technical and concise is "gatekeeping" for functional programming. If you are a frontend developer you need to know what the DOM is. If you are a Haskell programmer you need to know what a monad is. The fact that you don't get the Haskell jargon "for free" coming from other programming disciplines doesn't mean that it's "gated", it just means you need to learn. And there are plentiful resources to help you with that, countless "how to understand monads" articles and so on. In my experience, the Haskell community is perhaps the single most welcoming and helpful programming community. There's even a handy guide with big, friendly drawings.
- tomc1985 5y agoMy point wasn't so much a dig at FP as it was at programming in general. For example, I started my career writing Perl, which uses the word 'hash' to describe a dictionary-like datastructure. But Python uses the word 'dictionary' to mean the same thing. Javascript's equivalent is an 'object', though it is effectively a different. Other languages call this datastructure a 'map', others an 'associative array', and so on. It's like, effing pick one! And if you have to have multiple why do they have to overlap with other completely different things? When I first read about a 'map' in this context I was very confused. You also see this a lot comparing Microsoft environments to everyone else. Reading microsoft docs in the late 90s (which is where I first learned a lot of these concepts) you learn one set of vocabulary, and then in the _nix and webdev worlds you learn a whole different set. Take 'databinding', for example... I have not once heard this term used discussing shadow DOM stuff like React or Vue, yet databinding is exactly one of the services they provide. But no, they have to call it something different, making a lot of prior-art type research much more difficult than it needs to be. Perhaps it is part of the reason why the webdev world seems so intent on pointlessly repeating history. It hampers discussion and contributes to the conceptual isolation and tribalism that seems so common in this field. (And in the perl example, yes I know it's short for hashtable and that this is because this dictionary-like thing is exactly that, but I think that from an end-user's point of view it would be best if all these different terms were unified)
- lallysingh 5y agoHonestly most of what I've learned in category theory is just terminology. 2 Dozen flash cards would really take all the mystery away
- brundolf 5y agoAnd for those of us who struggle with rote memorization, it's a huge artificial barrier. Though in my experience even the "flash cards" are hard to find on the internet.
- tinco 5y agoIt's not an artificial barrier, you just didn't learn anything useful yet. So now you know what the words mean, but they mean super mundane stuff that you've probably dealt with a hundred times. You already knew how to use map and array concatenation before you even heard of the word monad. There's some magic to these words, but that magic is in how they're used to create new software, real things you can do with them that are easier to talk about when you've got single words for the concepts instead of vague descriptions. People in Haskell do things like having concatenating to an array be the same syntax as executing two statements sequentially. That's sort of weird, and probably useless, but the whole idea that two sequential statements are basically a monadic operation over the state of your computer, and maybe over the entire world was a new idea in the 90s and it captured something in the formal domain that we hadn't really captured as well before I believe. edit: I'm not trying to convince you that monads are important or even useful in general, I hope it doesn't come across that way. My favourite programming languages don't have monads as a fundamental concept, Haskell fell from my list a couple years ago. It's an interesting way of thinking about types and computing and I think that makes it more interesting than just a bunch of dense definitions for mysterious looking names.
- brundolf 5y agoI should be clear: you don't have to sell me on Haskell or these other concepts. I would love to add all that stuff to my toolbelt. I'm suggesting that the zoo of new terminology makes the effort barrier to doing so much higher than it needs to be. I'm sure some of it really is warranted, but my experience looking at formal math and logic is that they make things a lot harder than they need to be by introducing terms and symbols that stand-in for intuitive concepts and require unnecessary mapping back and forth between what you're reading on the page and the underlying concepts.
- Sniffnoy 5y ago> Wait, a functor is just a pure unary function? And an endofunctor is just one of those where the argument type and return type are the same? No. I don't know why the article talks about ordinary functions on values as if they're functors. They're not (or at least, not in a nontrivial way; you could come up with a silly, useless way in which they are, but that's not what anyone's talking about). Sorry, but it's the article that's misleading you here. Roughly speaking, in the programming context, a functor is a generic type with a map operation. E.g., arrays; you can map a function over an array by applying it to each element. Since there's only one category in play here, these functors are endofunctors of that category; one doesn't normally say "endofunctor" in this context since it doesn't convey any additional information. (As opposed to "a monad is a monoid in the category of endofunctors", where you want to specify that, because that statement is true beyond just the programming context.)
- contravariant 5y agoMathematically the main thing stopping it from being a function is that categories are typically too big to be a set. Otherwise they're pretty much the same as a function. The whole type thing is just something Haskell does, it's not something that's meaningful for general categories.
- thinkharderdev 5y agoNot really, mathematically a functor is really two "functions," one mapping objects to objects and one mapping morphisms to morphisms, along with some conditions that make sure the functor is "structure preserving." It is fundamentally a type level construct because, as it is applied to programming, the category in question is the category of types (which is not technically a category due to the halting problem but we sort of hand-wave that away).
- contravariant 5y agoSure a functor satisfies more conditions but that doesn't make it less of a function. Heck if you restrict it to mapping a single set of objects or a particular Homset it is a function, you just can't extend this to the whole category if the objects form a proper class (which happens fairly often). The whole distinction between value-level or type-level is meaningless in general categories. Sure there's a distinction between morphisms between objects and functors between categories but you've also got the category of categories where the morphisms are functors between categories. Edit: I'll have to concede that the article confuses them too much though, you can't just pick a random function and declare it to be a functor, especially when it's not clear between which categories.
- chrisseaton 5y ago> Wait, a functor is just a pure unary function? And guess what another name for a unary functions is... 'monadic'.
- lmm 5y agoWhich means something completely different from the use of "monadic" here.
- Twisol 5y agoTo expand on this, because it's actually fascinating... Functors capture a particular pattern of "pipelining": a series of operations A -> B, B -> C, etc. can be uniformly assembled in the context of any arbitrary functor F, ultimately giving a unary pipeline `F A -> F C`. (I won't detail what this means completely; I'm going for a gut instinct in the following bit.) But what if you have a binary function, (A1, A2) -> B? Functors only let you assemble unary operators; there's no provision for taking the outputs of two pipelines and weaving them together. Applicatives (or, "monoidal functors") give you an additional operator that let you turn a pair `(F A1, F A2)` into a contextual pair `F (A1, A2)`, which can then be fed to a unary pipeline. (In terms of pipe shapes, it's a Y-bend.) Monads extend this in a less interesting way, but in the sense of "monadic" as "unary", functors are actually more monadic than monads!
- lmm 5y ago> Wait, a functor is just a pure unary function? No. > And an endofunctor is just one of those where the argument type and return type are the same? Yes - "endo" means that in general. E.g. endomorphism. > Sometimes it makes me mad that so much confusion (and as another commenter put it, gatekeeping) has been sown in FP circles through the invention of pointlessly-obfuscated terminology for everything (and through - among many in those circles - a lack of interest in connecting the very simple dots that would allow masses of people to understand these concepts). It's like when the medieval Catholic Church refused to print a bible in anything but Latin so that they'd remain the sole authority on its content. > Kudos to the OP for cutting through some of that, but shame on those who maintain this status quo. > Edit: assuming it really is that simple. Some in the comments have suggested parts of the OP were wrong, so I'm curious to see discussion. The precise terminology is vital; it's how we avoid these errors when talking about this very abstract stuff. Given that the OP is very wrong, precisely because of the kind of attitude you've taken here, I hope you'll take this opportunity to reconsider your views.
- void_mint 5y ago> The precise terminology is vital; it's how we avoid these errors when talking about this very abstract stuff. Given that the OP is very wrong, precisely because of the kind of attitude you've taken here, I hope you'll take this opportunity to reconsider your views. Your behavior is precisely the behavior that someone new to FP would see and say "Hard pass on that", which goes into the greater problem of FP being unapproachable to beginners because of the unnecessary gatekeeping. If you cannot communicate with a beginner in language they understand, A.) you don't really understand it either, B.) you're just pushing them away from whatever you're advocating.
- lmm 5y agoCommunication is a two-way street. When someone is making an effort to learn, most FP advocates will happily make an effort to teach. But someone who comes in with guns blazing doesn't get to complain about being met with hostility, and if telling someone they're wrong when they're wrong is gatekeeping then it's a kind of gatekeeping that we need.
- dwohnitmok 5y ago> a functor is just a pure unary function? No, although a pure unary function could be viewed as a functor, the other direction doesn't hold. The common FP definition of a functor is anything that supports a notion of `map` (subject to the restriction that `map`ping the identity function does nothing, this is why a Functor is slightly more than just a Mappable). This is not a unary function although map takes a unary function as an argument. The more general mathematical definition is a mapping between categories. Categories are sufficiently general that you can view almost anything as a category, which is why a unary function can be thought of as a functor (because a functor can be a mapping between categories that happen to be sets). But a functor is both more general than a unary function and, in a certain sense, preserves more structure than a function, since it must preserve the structure of the underlying categories whereas functions generally only care about sets.
- tantalor 5y ago> why a Functor is slightly more than just a Mappable Explain that again?
- opnitro 5y agoA Mappable would be anything that has a `map` function. A `Functor` is a something that has a `map` function _AND_ obeys the rule that calling `map` with the identity function produces the same result. ie: say I have some value `f` that is a functor. If I call `map identity f` I should always get back `f`. The mere existence of a `map` function doesn't imply this law holds. The identity function always returns it's argument unmodified. (identity x = x) (Now, in Haskell the type system does not encode that law, but not following it would be breaking the interface.) If this explanation doesn't make sense or you need more examples I'm happy to expand EDIT: Both me and grandparent forgot another law for functors. They also need to preserve composition. fmap (f . g) == fmap f . fmap g In english, if I combine the functions f and g and map them, it should be the same as if I first map g and then map f
- dwohnitmok 5y ago
- jkhdigital 5y agoFunctors are type constructors, not functions (which map values). But you could be forgiven the misunderstanding, because it is easy to trip over this distinction given the identical syntax. So, to make this absolutely clear, a monad is a type constructor in functional programming. This is why I personally prefer the more “creative” explanations of monads, because they attempt to explain what the monad is doing to the original type: adding context, or the possibility of a null value, or multiple values, or a probability distribution over values, etc.
- epolanski 5y agoI don't like creative explanations. Definitions are easier. A functor is a PAIR, please notice the emphasis, of a type constructor F and a map function with the following signature: a -> b -> f a -> f b. Its not a type constructor, it's a pair of two things, the type constructor AND its map function. Now that we said what it is we provide the usual simple example of Array, Record or Option, show the type constructors and show how we map them. Is it really so hard to grasp starting from definitions rather than examples and contrived blogs? I wish people understood that fp is much easier to understand by reading definitions.
- brundolf 5y ago> I wish people understood that fp is much easier to understand by reading definitions. The real landscape of its adoption begs to differ. For the great majority of people, examples and analogies are fantastically more efficient for communicating ideas than building up from first principles. You get to re-use existing mental machinery, intuition, etc, until enough experience has been gained to refine it.
- Twisol 5y ago> Wait, a functor is just a pure unary function? A functor has two pieces: a pure unary function from types to types, and a pure unary function from functions to functions. The article doesn't talk about the function on types; it doesn't even talk about the function on functions. Its first example of a functor is the function `(num) => "" + num`, which isn't a functor. They map values of Integer to values of String, but they do not map any type to some type, nor do they map any function to some function. Neither part of a functor is present nor nearby. As an example, for every type T, there is a (non-generic!) type of lists with elements of type T. The non-generic part is really, really important, and is probably a big source of confusion. When we define, say, `class List<T>`, we're defining a whole family of independent types, which we might concretely name List__Integer, List__String, and so on. The List<T> notation is a way of getting that specific type without knowing what its name is. By abuse of notation, we (programmers, not category theorists) talk about List<T> as though that were the name of the type. Theorists separate the two notions, and consider `List` to be an actual function from types T to types List__T, whatever that type actually is. Most languages that let you define a family of types don't let you refer to the type function on its own. Java (say) never lets you refer to the List function on its own; the name List refers to some other shameful and unrelated concrete, non-generic type. When your language does let you speak about the type function itself, and pass it around to other things, we say that it has "higher-order types". (Just as a language that lets you speak of and pass around a function `factorial` without invoking it has "higher-order functions"; notwithstanding closures, that's what "higher-order" means. [0]) Just having a type function is not enough to have a functor. You also need a function that takes transformations between two types, T and S, to transformations between whatever types the type function sends them to. (For List, we said they're called List__T and List__S, but we can always refer to them indirectly by saying "whatever List maps T to", which is generally written F<T> for any type function F.) Because of the abuse of notation, we collectively "forget" that `List<T>` defines a function from types to types, and pretend that a functor is just defined by what it does to functions from T to S. But most of the point of functors in functional design is that you can pass around the type-function part, `List`, with the knowledge that it describes a mappable type. If you don't pass around type functions, the concept of a functor will be useless to you. [0] https://stackoverflow.com/a/6427289/159876 https://stackoverflow.com/a/6427289/159876
- lhorie 5y agoYou see, there's a bit of irony here in characterizing jargon as gatekeeping, because in the author's attempt to distill it, some pretty critical stuff got lost in translation. I share the sentiment that FP jargon can be impenetrable to the uninitiated, but the same can be said about programming jargon to non-techies: it may sound like gatekeeping to talk about "closures" but there's technical reasons why closures are called closures and global state being accessed by functions are not. The same goes for FP. Saying `val => val + val` is a functor because it maps numbers to numbers isn't really correct: you could arguably only say that in an universe where `val + val` is the only possible operation, and even then, that's not really correct because the utility of the function is hardcoded (meaning that if we were to hypothetically introduce an axiomatic operation, the internal consistence of this new algebra would implode). In a more useful algebra in a regular universe, the mapping must hold for any valid operation for that type. So a functor for the `number` type ought to look more like something like `Number.prototype.map = function(f) {return f(this)}` (i.e. the mapping contract holds for addition, subtraction, and all other algebraic operations that are valid on numbers contained within `f`) There are also practical technical things the article glossed over: Array.prototype.map is not monadic. The signature of the callback is `(T, number, Array<T>) => U`, which is most certainly not unary (and for that matter, using JS to illustrate FP is fundamentally problematic since JS isn't side-effect-free to begin with). If you think that's nitpicky, one fundamental thing with FP is that composition relies heavily on algebras not breaking on corner cases. This is why, for example, fantasy-land specifies a different method name for `map` (called `fantasy-land/map`); because `map` is already taken by Array and it breaks some composition patterns (notoriously, `[1,2,3].map(parseInt)`)
- brundolf 5y agoI absolutely think this is nitpicky, and a perfect demonstration of the pedantry I'm talking about: > Array.prototype.map is not monadic. The signature of the callback is `(T, number, Array<T>) => U`, which is most certainly not unary (and for that matter, using JS to illustrate FP is fundamentally problematic since JS isn't side-effect-free to begin with). The unspoken understanding in the OP is that we're only using .map() in the single-argument form. The author makes it clear that only a function with a single argument counts for their definition, and I feel there's also an implicit understanding that that function has to be pure. The fact that JavaScript technically lets you break both of these assumptions is entirely beside the point. For the sake of teaching, this idealized case is carved out for the example, and I for one had no misconceptions resulting from the relevant quirks of JS. We aren't writing a bullet-proof type system here, we're learning about concepts.
- WastingMyTime89 5y ago> Sometimes it makes me mad that so much confusion (and as another commenter put it, gatekeeping) has been sown in FP circles through the invention of pointlessly-obfuscated terminology for everything That's only Haskell. The other FP languages have sane communities.
- deleted 5y ago[deleted]
- brundolf 5y agoEdit 2: I'm sorry for getting a little inflammatory with my original comment. I have a headache today, and one of my biggest frustrations is needlessly poor communication of concepts, especially if it's willful, and especially if it wastes the potential of capable people who could otherwise be given understanding. With that said I may have gone on a bit of a rant here and made some generalizations, and I don't feel good about that aspect, so I apologize.
- kortex 5y agoYou remind me of when I started to actually get this stuff and it occurred to me that monads are just metadata+data with some special properties, and I posted about it on reddit and got downvoted to hell and told the many ways how that's wrong. The FP crowd...must be great at parties. I get this frustration. I think a big part of it is difference in definitions. There's the mathematician's monad, which is pure and abstract and obeys all these laws. And then there's the engineer's monad: an interface you can map, ap, and flatmap over. People get so hung up over "algebraic purity" they forget why they were invented/co-opted for software in the first place: to solve the problem of applying pure functions to side-effecty operations. > A monad is a wrapper. Anyone who tells you otherwise is being obtuse. https://rcrowley.org/2011/09/21/monads.html https://rcrowley.org/2011/09/21/monads.html
- pjc50 5y ago> Sometimes it makes me mad that so much confusion (and as another commenter put it, gatekeeping) has been sown in FP circles through the invention of pointlessly-obfuscated terminology for everything (and through - among many in those circles - a lack of interest in connecting the very simple dots that would allow masses of people to understand these concepts) This is why people say "a monad is a monoid in the Category of endofunctors" as a joke whenever anyone posts an unnecessarily complicated explanation. This is the biggest and most visible flashpoint between people who don't understand, and the people who don't understand why they don't understand and think that defining things in terms of other things that you also don't understand is a viable strategy.
- bitL 5y agoWelcome to higher math! You know, those PhDs and tenures need to be deserved, so why not obfuscating simple concepts to get ahead?
- qwerty456127 5y ago> It's like when the medieval Catholic Church refused to print a bible in anything but Latin so that they'd remain the sole authority on its content. I'd say reading the Bible, or any other deep philosophical/spiritual text in a translation is almost an utter waste of time (unless that actually helps you psychologically, then it is not), if not harmful. The original words and phrasing with all their possible meanings make a huge lot more sense than specific translations. Something that sounds banal or weird can actually mean something entirely different and deep once you carefully analyze the original words. It is, however, important to remember Latin is not the original for the Bibile. But it still has a lot of what has been lost in many other translations.