[HN Gopher] Functional programming jargon in plain English
___________________________________________________________________
Functional programming jargon in plain English
Author : mgliwka
Score : 471 points
Date : 2022-07-01 10:00 UTC (3 days ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| naillo wrote:
| "A homomorphism is just a structure preserving map. In fact, a
| functor is just a homomorphism between categories as it preserves
| the original category's structure under the mapping."
|
| Oh ok. Plain english.
| psychoslave wrote:
| Yue Shu haShou rareta
| marcosdumay wrote:
| Well, it's not nested.
| eddyschai wrote:
| Maybe if I explain what a Monad is in Plain English it'll help
| you understand functors?
|
| A monad is just a monoid in the category of endofunctors.
| mypalmike wrote:
| I think you need to explain endofunctors.
|
| An endofunctor is the category containing monoids such as the
| monad.*
|
| *This is probably wrong. Please don't explain.
| the_only_law wrote:
| The closest I've come to understanding Monads was funny
| enough, the Wikipedia page (ok so there's at least two pages
| on Wikipedia for Monads, one in a category theory context,
| and one in a more general programming context, the latter I
| could actually begin to grasp). It wasn't overly full of
| vocabulary from category theory, nor did it try to compare it
| to taco bell menu items.
| IncRnd wrote:
| If I may also quote!
|
| A monad is a special kind of a functor. A functor F takes
| each type T and maps it to a new type FT. A burrito is like a
| functor: it takes a type, like meat or beans, and turns it
| into a new type, like beef burrito or bean burrito.
| cratermoon wrote:
| Best comment in this thread. The guide does a so-so job of
| building on terms previously defined. The term accumulator, for
| example, is not defined before use. The first appearance of it
| is under Catamorphism, where the guide says, "A reduceRight
| function that applies a function against an accumulator and
| each value of the array (from right-to-left) to reduce it to a
| single value."
|
| I note, in passing, that the actual guide is just titled
| "Functional Programming Jargon". It does not claim to be "in
| plain English".
| jtdev wrote:
| rbonvall wrote:
| Let me present this intuition I've developed.
|
| * You have a producer of Cs, then you can turn it into a producer
| of Ds by post-processing its output with a function g: C - D.
|
| * You have a consumer of Bs, then you can turn it into a consumer
| of As by pre-processing its input with a function f: A - B.
|
| * You have something that consumes Bs and produces Cs, then you
| can turn it into something that consumes As and produces Ds using
| two functions f: A - B and g: C - D.
|
| With pictures: http://mez.cl/prodcons.png
|
| If you understand that, you understand functors:
|
| * producer = (covariant) functor;
|
| * consumer = contravariant functor;
|
| * producer-consumer = invariant functor;
|
| * post-processing = map;
|
| * pre-process = contramap;
|
| * pre- and post-processing = xmap (in Scala), invmap (in
| Haskell);
|
| * defining how the pre- and post-processing works for a given
| producer or consumer = declaring a typeclass instance.
|
| It doesn't mean that "a functor _is_ a producer ", but the
| mechanics are the same.
| ncmncm wrote:
| This is more helpful than anything I have ever encountered on the
| topic.
|
| Comments:
|
| 1. It should explain map somewhere before it is used.
|
| 2. For the more abstruse and abstract concepts, a comment
| suggesting why anybody should care about this idea at all would
| be helpful. E.g., "A is just a name for what [familiar things] X,
| Y, and Z have in common."
|
| 3. It goes off the rails halfway through. E.g. Lift.
| sanderjd wrote:
| Expanding on your #1, I think they could use some more
| definitions. As you say, they use "map" in its functional
| programming sense before defining it, but I think more
| confusing is this one:
|
| > _A category in category theory is a collection of objects and
| morphisms between them._
|
| What is a "morphism"?
|
| I think this is a great starting point though, which could use
| some expansion.
| ncmncm wrote:
| Ja, that "morphism" bit matches my #3.
| goto11 wrote:
| Pet peeve: The word "just" when used to gloss over something with
| the author don't know how to explain. Using "just" shift the
| burden from the author to the reader, since it signals it is the
| readers fault if they don't understand.
|
| > A homomorphism is just a structure preserving map. In fact, a
| functor is just a homomorphism between categories as it preserves
| the original category's structure under the mapping.
|
| How about removing the "just":
|
| > A homomorphism is a structure preserving map. A functor is a
| homomorphism between categories as it preserves the original
| category's structure under the mapping.
|
| Much clearer. Although most readers would now ask what
| "structure" and "structure preserving" means, since this is never
| explained.
| josu wrote:
| Be the change you want to see in the world haha
|
| https://github.com/hemanth/functional-programming-jargon/pul...
| bjarneh wrote:
| > The word "just" when used to gloss over something with the
| author don't know how to explain.
|
| Never thought of that before, but it's certainly true. Always
| hated when we got cryptic explanations for the difficult
| things, and elaborate explanations for the stuff everyone
| understood anyway at the university. I guess professors have to
| explain things they don't fully understand from time to time
| cinntaile wrote:
| This is left as an exercise to the reader.
| minraws wrote:
| Just to be clear, that's quite literally the Wikipedia
| definition.
|
| > In algebra, a homomorphism is a structure-preserving map
| between two algebraic structures of the same type (such as
| two groups, two rings, or two vector spaces).
|
| Not sure if this is rude but many definitions there seem a
| bit, not plain English.
|
| I am assuming we are aiming for something close to ELI5 if
| the title says plain English.
|
| Ofc that could not be what the author intended and is just
| but that's how inferred the title.
|
| I will see if I can find time to improve and send some PRs
| for the ones that are in deep need of simplification,
| hopefully OP is open to discussing changes.
|
| Also like Arity, Arity is not just for functions, it can
| sometimes be interchanged with Rank and apply to even Types.
| higher ranked types. higher arity types... I do understand
| they are not something most people like, because of their
| issues but that's not a complete definition so I assumed it
| was for the means of simplifying to explain to beginner
| programmers. [reference to issues with HRTs](https://www.scie
| ncedirect.com/science/article/pii/S016800729...)
|
| Again this is not meant to be rude to the author, just
| hopefully the title could be better formed to explain the
| intent of the work. Or my opinion might be minority and we
| can decide against it as well ofc.
| mjburgess wrote:
| The former means: all Hs are SMPs ; the latter may also mean:
| some Hs are SMPs.
|
| The former is clearer. Adjust your understanding of `just`,
| it's definition 4a at
| https://www.britannica.com/dictionary/just
| marcosdumay wrote:
| As a hint, any time you read "just" in mathematics, replace it
| with "exactly". That may make it clearer.
| saithound wrote:
| Mathematicians use "just" with a specific meaning: it is not
| used to gloss over something that the author doesn't know how
| to explain. It has a purpose, useful for mathematically trained
| readers.
|
| For suc a reader, "a homomorphism is just a structure
| preserving map" makes it clear that "homomorphism" and
| "structure-preserving map" can be used interchangably, and that
| by understanding one of the concepts, you'll immediately
| understand the other as well.
|
| When you got rid of the word "just", you got rid of this
| connotation and changed the meaning of the sentences.
|
| E.g. the sentence "a functional is a linear transformation" is
| correct; but not all linear maps are functionals, so writing "a
| functional is just a linear transformation" would be plain
| wrong in a mathematical setting.
| 8note wrote:
| I don't understand who the audience for this glossary is.
|
| Math students coming from another discipline?
|
| Only some people will understand the technical meaning of
| "just" as intended
| plasticchris wrote:
| Sure, but the title does say plain English so this would be
| the sort of thing the author is trying to avoid. If that's
| the meaning just write all a are b.
| saithound wrote:
| While I agree that the English used in the repo could be
| plainer, that's not remotely the same thing as "using
| 'just' to gloss over something that the author doesn't know
| how to explain", which was the complaint that goto11 made.
| nabla9 wrote:
| Both you and goto11 make a good point.
|
| What is needed is "Mathematical English in plain English."
| minraws wrote:
| This has not been true in my experience, as someone who is
| used to talking to a mathematician during my academic life (a
| bit). They prefer to not use words like "just X".
|
| They generally reply in definite statements, "this is
| undecidable", "this is an example of X", "this can't be done
| without Y", they rarely say. "X is just Y", they would
| probably say, "X is Y".
|
| The "just" implies some form of detail that might be missing
| in the relation generally. Even my mathematical text books of
| pretty advanced topics rarely used "just".
|
| Again, what you say is most likely true when math people talk
| amongst themselves but I don't think they do so with other
| non math people. I was in compsci.
| mjburgess wrote:
| "just" is used across technical academia (including
| philosophy) to mean "nothing over and above"
| lachenmayer wrote:
| That would be an extremely good thing to explain in the
| introduction. I feel like this kind of very culturally-
| specific usage of everyday words is what makes math/CS papers
| so impenetrable for many people.
|
| The word "easy" is another example: saying "it is easy to
| show X" (just?) means that X can be derived from the already
| stated theorems in a more-or-less mechanical way without
| having to introduce new concepts. It does not in any way
| suggest that deriving this will be "easy" for a student
| reading the paper.
|
| (Of course the most challenging "easy" parts are best left as
| an exercise for the reader anyway...)
| jasode wrote:
| _> Mathematicians use "just" with a specific meaning: it is
| not used to gloss over something that the author doesn't know
| how to explain. It has a purpose, useful for mathematically
| trained readers._
|
| Based on the author's public profile, I'm not convinced the
| author is a mathematician writing _"... is just ... "_ as
| rigorous formalizations for a math-trained audience.
|
| Instead, the _" is just"_ phrases are innocently slipping
| into explanations as a subconscious verbal tic caused by the
| The Curse of Knowledge. My previous comment on that
| phenomenon: https://news.ycombinator.com/item?id=28256522
|
| (Also, as a sidebar followup to your comment, Wikipedia's
| page about homomorphism
| (https://en.wikipedia.org/wiki/Homomorphism) has this as the
| first sentence: _" In algebra, a homomorphism is a structure-
| preserving map ..."_
|
| I'm guessing that a hypothetical edit to _" a homomorphism is
| _just_ a structure-preserving map"_ -- in an attempt to add
| more refinement and precision to the definition... would be
| rejected and reverted back by other mathematicians.)
| jacquesm wrote:
| "Category
|
| A category in category theory is a collection of objects and
| morphisms between them. In programming, typically types act as
| the objects and functions as morphisms."
|
| Much clearer now...
| mjburgess wrote:
| These definitions don't really give you the idea, rather often
| just code examples..
|
| "The ideas", in my view:
|
| Monoid = units that can be joined together
|
| Functor = context for running a single-input function
|
| Applicative = context for multi-input functions
|
| Monad = context for sequence-dependent operations
|
| Lifting = converting from one context to another
|
| Sum type = something is either A or B or C..
|
| Product type = a record = something is both A and B and C
|
| Partial application = defaulting an argument to a function
|
| Currying = passing some arguments later = rephrasing a function
| to return a functions of n-1 arguments when given 1, st. the
| final function will compute the desired result
|
| EDIT: Context = compiler information that changes how the program
| will be interpreted (, executed, compiled,...)
|
| Eg., context = run in the future, run across a list, redirect the
| i/o, ...
| jmgimeno wrote:
| Yes, but you're relying on the undefined concept of context.
| How do you define context?
| mjburgess wrote:
| "The idea" practically, is whatever resolves the
| polymorphism. So context is a compiler function from
| syntax->semantics.
|
| Eg., `+` is polymorphic, `1 + 1` and `"1" + "1"`... the
| _context_ is whatever implicitly resolves `+` to `ADD` or to
| `CONCAT`.
|
| The utility of these ideas is, in practice, just they provide
| a syntax for different kinds of polymorphism. Eg., for
| monads, we're basically just telling the compiler to change
| its implementation of `;`
| shikoba wrote:
| > Functor = context for running a single-input function
|
| No a functor is a "function" over types that can transport the
| arrows:
|
| (A -> B) -> (F A -> F B) for a covariant functor
|
| (A -> B) -> (F B -> F A) for a contravariant functor
|
| With the sum and product there is also the exponential:
|
| B^A = functions from A to B
| stillkicking wrote:
| This explanation is useless imo because it expects the reader
| to already understand the weirdest part:
|
| "If I have a function A -> B, how could I possibly get
| something that goes in the direction F B -> F A out of it?"
|
| The answer to this is: given e.g. a function that accepts a
| B, i.e. `B -> ...` you can compose it with `A -> B` on the
| _input side_, to get a function that accepts an A, i.e. `A ->
| B` (+) `B -> ...` = `A -> ...`.
|
| Once you've managed to get that across, you can start talking
| about contravariant functors. But expecting people to just
| intuit that from the condensed type signature is pedagogical
| nonsense.
| shikoba wrote:
| Saying without examples is a pedagogical nonsense, I can't
| argue, because I agree.
|
| But if I provide the two typical examples Hom(A,) and
| Home(,A) and how they transport the arrows it starts to
| click.
|
| There is also List which is a covariant functor. The stupid
| Home(unit,) which is the "identity" functor.
|
| I could also add Hom(Bool,) which is the pair functor, ie
| Hom(Bool,A) is a tuple in AxA. A->AxA (or A->Him(Bool, A))
| is a covariant functor.
|
| Etc. Examples are the keys to understand the idea.
| mjburgess wrote:
| In my simple language, `F` is that context. And (A->B) is a
| single-argument function. // Int -> Float
| oneArgFn(x) = x + 1.1 // List of Int -> List of
| Float ctxOneArgFn(xs) = ListFunctor(oneArgFn, xs)
| //ie., map()
| reuben364 wrote:
| Single-argument doesn't capture the difference between
| functor and applicative. ex1 :: Functor f
| => f (Int, Int) -> f Int ex1 = map (uncurry (+))
|
| Applies a multi-argument function within a functor. What
| applicative allows you do is take a product of contexts to
| a context of products, and lift a value to a context.
| pure :: a -> f a zip :: (Applicative f) => (f a, f b)
| -> f (a, b)
|
| This can then be used to make ex1 into a multi-argument
| function of contexts, which is not the same as (uncurry
| (+)) being a multi-argument function ex2 ::
| (Functor f) => (f Int, f Int) -> f Int ex2 = ex1 .
| zip
| mjburgess wrote:
| Sure, it's a particular interpretation of `(f a, f b) ->
| f (a, b)` (a, b) is an a -> b
| (f a, f b) is an f a -> f b
|
| Applicative, over an above this, is really just useful
| when you have (f a, f b, f c, ..)
| shikoba wrote:
| You're just reducing my idea to a specific case. You missed
| the generalized idea.
| init0 wrote:
| Send a PR to fix it!
| shikoba wrote:
| There are so many things to fix. And they should have use
| coq as default language.
| HelloNurse wrote:
| A product type, named after Cartesian product, _contains_ other
| objects of various types, but it is none of A, B or C itself.
|
| If types A, B and C have respectively a, b and c distinct
| values, their sum type has a+b+c values (each value of each
| type is allowed) and their product type has abc values (the
| part of the value that belongs to each type can be chosen
| independently).
| mgaunard wrote:
| Currying is just systematic partial function application for
| each argument.
| yowlingcat wrote:
| I find your list a lot more helpful (and succinct) than the
| linked repo in the thread topic. Kudos! Perhaps you should make
| your own repository -- I would upvote it!
| init0 wrote:
| Send a PR to the repo!
| stingraycharles wrote:
| I completely understand what you're saying, but assuming that
| this guide is aimed at people entirely unfamiliar with these
| concepts, I'm not sure whether these "ideas" provide any
| meaningful explanation to them.
|
| Demonstrating by example what e.g. currying actually looks like
| is much more powerful, at least from my point of view. In that
| regard, I'm actually pleasantly surprised this guide does a
| very good job at that.
| mjburgess wrote:
| Currying is one of those cases where the code _is_ the
| explanation
|
| I think in many cases this isnt right, eg., Monads. The
| reason flatMap() "flattens" is just that "flattening" is
| really just sequencing, denesting the type using a function
| requires a sequenced function call: f(g(..))
|
| This applies to many of these "functional design patterns"...
| theyre just ways of expressing often trivial ideas (such as
| sequencing) under some constraints.
| ncmncm wrote:
| We need an explanation why we should care about support for
| currying. Where it is really just to support variadic
| argument lists, it is a big hammer for a little problem.
| stingraycharles wrote:
| And precisely because of the code sample, it should be
| obvious that currying is _not_ the same as variadic
| function arguments.
|
| Instead, it allows for very concise partial function
| application, as demonstrated by the code. I can just call
| any function with a subset of its arguments, and it
| automatically returns a "new function" which you can call
| with the remainder of the arguments. It allows you to
| reason about calling functions in a different way.
|
| Currying is one of those concepts done very well in
| languages such as Haskell (not a coincidence, as both
| derive their name from Haskell Curry).
|
| But saying it's a "big hammer to implement variadic
| function arguments" is like saying that pattern matching
| is a big hammer to implement a switch statement. Yes, it
| provides that functionality, but also much more in a very
| elegant manner.
| ncmncm wrote:
| Saying _" it allows for very concise partial function
| application"_ does nothing but repeat the definition. It
| does not, in particular, offer any reason to want that.
| What is so special about the first argument, that I want
| to fix it? Why not the third? Why is what I do to fix the
| third not just as good for the first?
|
| Pattern matching is a good example of a language feature
| included because they could not figure out how to provide
| features expressive enough to be composed to implement
| the feature in a library.
| chowells wrote:
| In a language like Haskell, pattern matching was
| explicitly chosen to be a primitive operation. It's not
| that there's no way to put it in a library; it's that it
| was chosen to be one of the small set of ideas everything
| else is described in terms of. Along with allocation and
| function application, you've got the entirety of
| Haskell's evaluation model. (Note: not execution model.
| That needs a bit more.) Having such a small evaluation
| model probably should be taken as evidence the primitives
| were chosen well.
| a1369209993 wrote:
| > Note: not execution model. That needs a bit more.
|
| Actually... not really? You need the foreign function
| interface to have anything useful _to_ execute, but
| (unless you 're talking about something else?) the
| execution _model_ is basically just a State monad
| carrying a unique magic token, built on top of the same
| evaluation model as everything else.
| chowells wrote:
| The execution model needs _something_ that actually
| drives execution. There needs to be something that
| explains how IO actions are actually run. The state
| approach kind of works until you need to explain multiple
| threads, then it falls over.
|
| Edward Kmett did some work to describe IO as an external
| interpreter working through a free monad. That approach
| provides very neat semantics that include multiple
| threads and FFI easily.
|
| But IO needs something to make it go, and that something
| needs capabilities that aren't necessary for the
| evaluation model.
| a1369209993 wrote:
| > The execution model needs _something_ that actually
| drives execution.
|
| Er, yes: specifically, execution is drivenby trying to
| pull the final RealWorld token out of a expression along
| the lines of `runState (RealWorld#) (main)`, which
| requires the evaluation of (the internal RealWorld ->
| (RealWorld,a) functions of) IO actions provided by FFI
| functions (hence why you need FFI to have anything useful
| (or more accurately, side-effectual) to execute).
|
| > until you need to explain multiple threads
|
| I don't. It's a terrible idea that's effectively the
| apotheosis of premature (and ill-targeted) optimization
| as the root of all evil.
| Arrrlex wrote:
| Currying support is orthogonal to how function arguments
| work. Some languages (e.g. OCaml according to this
| question[1]) combine named parameters with currying,
| allowing partial application with any of the function's
| arguments.
|
| [1] https://stackoverflow.com/questions/3015019/is-there-
| a-progr...
| jerf wrote:
| I'm not trying to dodge your question, but the answer to
| your question is that until you work with it for a while
| you're not going to understand it. Any blog-sized, bite-
| sized snippet isn't impressive. You have to work with it
| for a while.
|
| I speak many computer languages, and one of the ways I
| measure them is, "what do I miss from X when using Y?"
| The answers will often surprise you; the thing you'd
| swear up and down you'd miss from X you may never think
| of again if you leave the language, and some feature you
| hardly consider while you're in the thick of programming
| in X may turn out to be the thing you miss in every other
| language for the rest of your life. For me, one of the
| things I deeply miss from Haskell when programming
| elsewhere is the fluidity of the use of partial function
| application through the currying syntax. I see so many
| people trying to force it out of Haskell into some other
| language but there just isn't any comparison to the
| fluidity of map someFunc . filter other
| . map (makeMap x y) $ userList
|
| Currying enables that syntax. If you don't see how, I'm
| not surprised. Sit down and try to write a syntax
| extension for an Algol-descended language that makes it
| work that well. Be honest about it. Run some non-trivial
| expressions through it. What I show above you should
| consider the _minimum_ level of expression to play with,
| not the maximum. If you pick something like Python to
| work in, be sure to consider the interaction with _all_
| the types of arguments, like positional, keyword,
| default, etc. It can absolutely be done, but short of re-
| importing currying through the back door I guarantee the
| result is a lot less fluid.
|
| The question about "what is special about the first
| argument" is backwards. The utility of the first argument
| is something you choose at _writing_ time, not _use_
| time. The reason why map 's first argument is the
| function to map on and the second is the list to map over
| is nothing more and nothing less than most of the time,
| users of map use it as I show above. There's no deep
| mathematical or group theory reason. If you don't like it
| there are simple functions to change the orders around,
| and the goal of picking function argument orders is just
| to minimize the amount of such functions in the code.
| Nothing more, nothing less.
| skybrian wrote:
| That's an example of why Haskell code is often hard to
| read, and why I'm glad this _isn 't_ available in other
| languages. Why not write out the callback given to map
| using a let block? Then you can give meaningful names to
| intermediate values so it's easier to see how the
| pipeline works.
|
| (Though, functional programmers would probably pass up
| the opportunity and use one-letter variable names.)
| Iceland_jack wrote:
| The feature is not called currying but partial
| application, it is very nice and intuitive.
| lgas wrote:
| Is this what you mean? let f = makeMap
| x y in map someFunc . filter other . map f $
| userList
|
| I suspect not as it certainly doesn't seem to improve the
| situation. Perhaps you could elaborate?
|
| Edit to add: I was focused on the "write out the callback
| given to map using a let block" part and not the
| meaningful name part, but that's perhaps what's confusing
| about this to me, as (makeMap x y) seems like the most
| clear name possible here, what else could you do? This?
| let mapOfXy = makeMap x y in map someFunc .
| filter other . map mapOfXy $ userList
|
| It only obscures things.
| ncmncm wrote:
| OK, thank you. The key seems to be that argument lists
| for functions in a language with easy currying are
| carefully designed so as to make currying maximally
| useful.
| jstanley wrote:
| Couldn't I "curry" a function f(x,y)
|
| to "partially apply" it on x=3 with just:
| function(y) { return f(3,y); }
|
| ?
|
| And then I've "partially applied" f. Is this what
| currying is?
|
| The syntax I posted is ambivalent about which arguments
| you're partially applying, isn't that superior to only
| being able to provide the first argument?
| Jtsummers wrote:
| Currying is turning a function with arity n into a
| sequence of unary functions. f(x,y) = ...
| // arity 2 f = \x -> \y -> ... // sequence of two
| unary functions f x y = ... // more compact
| representation of the above
|
| Regarding partial application, your JS (?) example is
| basically what happens under the hood with Haskell, but
| without the ceremony: f x y = ...
| fByThree = f 3 fByThree y = f 3 y
|
| Those last two are equivalent, but you don't have to
| include the _y_ parameter explicitly, because _f 3_
| returns a function that expects another parameter (like
| the function you wrote out explicitly).
|
| And the Haskell version is more general, since it doesn't
| require a unique function to be written for each possible
| partial application. Of course, you can do this in
| languages with closures: function fByX(x)
| { return y => f(x,y); } fByThree =
| fByX(3);
|
| But in Haskell that extra function isn't needed, it's
| just already there and called _f_. Regarding your last
| statement, there are also combinators in Haskell that
| allow you to do things like this:
| fXbyThree = flip f 3 // equivalent to:
| fXByThree x = f x 3 // JS function
| fXByThree(x) { return f(x,3); } // or
| function fXByFixedY(y) { return x => f(x,y); }
| fXByThree = fXByFixedY(3);
|
| So I'm not sure it's _strictly_ superior, it is more
| explicit though.
| jstanley wrote:
| Thanks!
| urxvtcd wrote:
| While I _think_ I understand monads on some rudimentary
| level through how join and bind operate, your "just
| sequencing" doesn't tell me anything. And this is a problem
| with a lot of these texts. Maybe that's trivial to the
| writers, but it makes me feel even dumber when I cannot
| understand a concept I already understand, lol.
| dllthomas wrote:
| A big problem with "just sequencing" is that there are a
| lot of ways of sequencing that either don't require the
| complexity of a monad or for which the monad interface is
| insufficient.
| lgas wrote:
| The former is probably uncontroversial but I'd love to
| see some examples of the latter since my understanding
| was that monads are essentially the generalization of
| sequentiality.
| dllthomas wrote:
| One obvious place to look is things that are almost a
| monad but can't be lawful and which might nonetheless be
| useful. An example that comes to mind is trying to use
| Set as a monad (for nondeterminism like the list monad,
| but with deduplication), which you can't do for some
| potentially avoidable technical reasons (it would have to
| be restricted to things you can Ord) but more importantly
| Set can't be a monad because Set can't be a functor,
| because `x == y` does not actually imply that `f x == f
| y` (even for reasonable definitions of Eq), so `fmap (f .
| g)` might not be equivalent to `fmap f . fmap g`.
| lgas wrote:
| I thought it was the case that you can't implement the
| Monad type class using the built in Data.Set
| implementation of Sets but there's no reason in principle
| why you couldn't build a Set Monad that works that way,
| right? Perhaps I am mistaken so I will investigate
| further.
| dllthomas wrote:
| There _is_ a reason in principle that you can 't build a
| Set Monad that works that way: it violates the functor
| laws.
|
| In order to be a functor, you need `fmap (f . g) == fmap
| f . fmap g`.
|
| The problem is that some functions (for instance,
| Data.Set.showTree) can produce different results for two
| values that compare equal.
|
| For instance, imagine we have a set containing (only) the
| lists `[1,2]` and `[2,1]`; let's call it s. How many
| elements are in `fmap (Data.Set.showTree .
| Data.Set.fromList) s`? Two, because the result of
| fromList winds up being order dependent _in the structure
| of the internal tree_ , even though both Sets represent
| the same set of values. On the other hand, how many
| elements in `fmap showTree . fmap fromList`? Just one,
| because the Set has a chance to "see" the output of
| fromList and notice that they are equal according to Eq.
|
| Does this violation of the functor laws matter? Maybe;
| likely not; it depends on your use case.
|
| But mathematically it means that the thing we are working
| with is not a functor, and so the thing we are working
| with is also not a monad (as all monads are functors).
| shikoba wrote:
| I prefer to say that show tree violates the abstraction.
| You can have s1=s2 but not f(s1)=f(S2). So f (show tree)
| is not a real function (or = is not a real equal). But
| showtree is a debug function, it doesn't count. It's like
| saying in C that a=13 and b=13 isn't a real equality
| because &a and &b are not equal. There is the underlying
| structure and the structure after the equivalence. You
| have to know of what you're talking.
| lgas wrote:
| Looks like https://hackage.haskell.org/package/set-monad
| is an example.
| pdpi wrote:
| This is one of those things where looking at the type
| signature hard enough eventually gives the game away, but
| most writing on it sucks: bind :: m a
| -> (a -> m b) -> m b
|
| Because that function in the middle takes an `a`, your
| implementation of `bind` needs to be able to take an `m
| a` and pull an `a` out of it, which means it also has to
| evaluate however much of `m` is needed to actually get to
| that `a`.
|
| Because that function in the middle returns an `m b`,
| binding again with a function `b -> m c` requires you to
| pull `b` out of `m b`, which in turn forces pulling an
| `a` out of `m a` to make progress. This is where you
| force sequentiality -- you can only evaluate the `m` in
| `m b` after you've evaluated the `m` in `m a`
| samiskin wrote:
| This makes a lot more sense than anything I've read about
| this in the past, thanks for the explanation!
| pdpi wrote:
| Thanks, that's reassuring. I've long been frustrated by
| just how bad monad posts are in general, and been meaning
| to write something up about it that makes it clearer.
| It's good to know that I'm at least going in the right
| direction :)
| skybrian wrote:
| Caveat: there is the trivial case of calling the middle
| function zero times. Which does happen (in the case of a
| Maybe type) but presumably any useful code needs to be
| able to call the function sometimes.
| cratermoon wrote:
| Assuming that this guide is aimed at people entirely
| unfamiliar with these concepts, I think it fails to explain
| the concept. Yes, it is plain English, but it is not _simple_
| or _common_ English, and it 's mostly code examples. I'm
| familiar with the concepts, but not fluent, and functional
| programming jargon I already knew is not clearer to me now
| than it was before I read it.
|
| Good sample code, though. It only required me to look up a
| handful of things I wasn't already familiar with.
| b3morales wrote:
| I agree, and not just the English. There is use of
| particular notation that does not explain anything unless
| you already know what it means:
|
| > f is a morphism from a -> b, and g is a morphism from b
| -> c; g(f(x)) must be equivalent to (g * f)(x)
|
| If I don't already understand (g * f)(x) this is not
| helpful at all. This other one especially jumped out at me
| (perhaps because I've personally never seen this "bent
| equals sign" [?] before):
|
| > A functor must adhere to two rules:
|
| > Preserves identity
|
| > object.map(x => x) [?] object
|
| What does that sign [?] mean? If I don't know, I am no
| closer to understanding functors. And the sign is not
| explained anywhere in the document.
| wawjgreen wrote:
| irrational wrote:
| What is a unit?
|
| What does it mean to join together units?
|
| What is a context?
|
| What is a single input function? A function with a single
| parameter?
|
| Does multi input mean multiple parameters?
| AtNightWeCode wrote:
| Oneof? Discrete data? Fuzzy logic?
| ufo wrote:
| I get why they chose to do this in Javascript, but I can't help
| but feel it's an awkward choice. Many of the examples (e.g.
| currying) look unnatural in javascript.
|
| https://github.com/hemanth/functional-programming-jargon
| k__ wrote:
| I like it.
|
| Could need a bit of clean up. Some points could need more
| explanation, simpler examples, and every point could follow the
| same structure.
|
| But for a beginner, I think it's a pretty starting point.
| _benj wrote:
| Without being very versed in FP and having only read the first
| two (arity, HFO) this seems super helpful!!
|
| Even if imperfect and I need to research further, this glossary
| provides a way to attach new knowledge to existing one.
| Linda703 wrote:
| mgaunard wrote:
| Looked at first one (arity), saw "argument" when what was
| actually meant was "parameter", and therefore dismissed the
| document.
|
| If you want to make a glossary, at least try to be precise.
| mtreis86 wrote:
| They said 'a function takes arguments', which is correct. The
| arguments are passed into the function, aligning to and being
| bound to the function's parameters. So arity applies both to
| arguments the function can take, and the parameters the
| function has.
| mgaunard wrote:
| The way it's phrased and the examples makes it clear whoever
| wrote this didn't understand the distinction.
| ww520 wrote:
| I thought this guide is great. Not sure why people are upset with
| it. It's not the be all end all definite guide to the topic, but
| it definitely helps.
|
| I especially like the examples given that remove ambiguity in
| explaining the concepts.
| singaporecode wrote:
| All these niche functional programming languages are an exercise
| in pseudo intellectualism
|
| Give me an object oriented language any day. The world is made of
| state and processes, (modern niche) functional programming goes
| too far to derecognise the value of state in our mental models
|
| The good thing about functional programming is stressing to avoid
| side effects in most of the code and keep it localised in certain
| places...
| 62951413 wrote:
| I can relate to your sentiment but calling it " _pseudo_
| intellectualism " is a step too far in my opinion. The math
| behind FP is legit. Like Programming Language Theory it can
| teach you interesting abstractions. And useful basic things
| like how most collections you use are monoids, map/flatMap,
| etc.
|
| On the other hand, the original Design Patterns book was
| published in 1994. I'm still waiting for its FP equivalent.
| ratww wrote:
| It's kinda funny, because you could say exactly the same about
| object-oriented programming as a discipline.
|
| In functional programming the jargon might be foreign, but they
| correspond with the math they came from. You also have clear
| rules and clear situations where you can apply all those
| things.
|
| In object orientation, however, we have mostly made-up jargon
| (from patterns and from SOLID), the "rules" for the application
| of those are completely fuzzy, arbitrary and broken most of the
| time, and even the categorisation of those is mostly made-up.
| It's pseudoscientific-mumbo-jumbo compared to Monads and other
| FP silly-named stuff.
| globalise83 wrote:
| I guess you could also say that the world is made of state,
| state transitions and state derivations, and functional
| programming does a great job of making the latter two reliable.
| ncmncm wrote:
| Every _anything_ -oriented language amounts to wankage.
|
| The world is a complicated place with complicated problems.
| Complicated problems are always solved by splitting them into
| different sub-problems, each routinely of radically different
| character than the outer problem, other sub-problems, or its
| own sub-sub-problems.
|
| So, a language "oriented" for any one of those is a terrible
| choice for others. To solve any whole problem, your language
| should support all the styles that might be useful. Often, a
| sub-problem falls to a style combining a little of this style
| and a little of that. Styles should not be straitjackets.
|
| Generally, it is better if the language is powerful and
| expressive enough that support for a style is not built in, but
| can be provided by a library. Any fancy built-in feature is
| always an admission that the core language wasn't strong enough
| to express it in a library.
|
| That is why weak languages so often have fancy core features
| (e.g. dictionary): without, you would be left with no support.
| This should make us suspicious of a "match" feature.
|
| A more powerful language lets you pick from variations on the
| fancy feature in a library or libraries. And, the library might
| define a new style useful for the sort of problems the library
| is designed for.
| krageon wrote:
| > The world is made of [...]
|
| Either define concretely what the world is made of (i.e.
| particles and what they do), or don't use this sentence.
| Currently you just say "wow this is so abstract, it's actually
| <something else that's also abstract>". Turns out neither
| paradigm has anything to do with real life, they have their own
| niches and their own place within different contexts.
| rswail wrote:
| Functional programming is actually mathematics based on lambda
| calculus.
|
| Imperative programming isn't.
|
| OOP is a failed metaphor, unless you use composition, not
| inheritance, even then, the actual basis for OOP was about the
| messages between objects, not the internals.
|
| > The world is made of state and processes
|
| No, the world is made of objects that have state and messages
| (events) between them.
| jimbob45 wrote:
| The lambda calculus is an entirely arbitrary way to organize
| things in math. It's not based on nature or truth at all.
|
| The real problem, though, is that FP doesn't do anything
| well. It's never the fastest method of programming, which
| means that it needs to excel in some other way for its
| proponents to be right about it. Is it the most maintainable?
| Maybe if you have zero side effects but then any paradigm
| would be in that case. Once you introduce state, it becomes a
| nightmare to maintain, unlike OOP. It's certainly not the
| most readable.
| ebingdom wrote:
| > The lambda calculus is an entirely arbitrary way to
| organize things in math. It's not based on nature or truth
| at all.
|
| Lambda calculus, category theory, and logic are essentially
| 3 sides of the same coin (the Curry-Howard-Lambek
| correspondence). The rules of lambda calculus match those
| of natural deduction. It runs quite a bit deeper than
| you're suggesting here. It's not just some arbitrary
| formalism.
| dboreham wrote:
| Glad to see these comments because the parent's "it's
| math therefore from a deity, and much more correct than
| your grubby computer stuff" is so often trotted out. It
| turns out that there's no directionality to the
| relationship between mathematics and computers -- they're
| the same thing and one can be transformed into the other.
| e.g. Lambda Calculus is just a kind of VM someone came up
| with that can be used to model certain structures. Same
| for Category Theory, and same for Logic. So you might as
| well say that Lambda Calculus is derived from IBM 360
| assembler language than the other way around.
| garethrowlands wrote:
| There are limits on what lambda calculus can model though.
| For example, it doesn't model IO very well. So, these days,
| lambda calculus is only one of the parts of maths that
| informs functional programming.
| [deleted]
| lordnacho wrote:
| Doesn't every mainstream language these days allow you to pick
| what style you want according to the situation?
| ncmncm wrote:
| That practically defines mainstream language, alongside
| having been blessed with a non-repeatable miracle.
|
| But C, Java, and Go don't.
| ris wrote:
| And no explanation of fixed points.
| Jemm wrote:
| As someone who learned coding in the days of Pascal and Fortran;
| can I just say: WTF and why!
| Latty wrote:
| Lists like this tend to be a bit overwhelming without context,
| because a definition can often seem to focus on things that don't
| make sense if you don't understand the use case, even if you
| understand the words.
|
| Even if you've read the definition of functor first
|
| > Lifting is when you take a value and put it into an object like
| a functor. If you lift a function into an Applicative Functor
| then you can make it work on values that are also in that
| functor.
|
| Is a pretty rough sentence for someone not familiar. I think Elm
| does a pretty good job of exposing functional features without
| falling into using these terms for them, and by simplifying it
| all.
|
| It does pay for that in terms of missing a lot of the more
| powerful functional features in the name of keeping it simple,
| but I do think it makes it a great entry-point to get the basics,
| especially with how good the errors are, which is very valuable
| when you are learning.
|
| I know it's a controversial language on HN to some extent (I
| certainly have my own issues with it _shakes fist at CSS custom
| properties issue_ ), but I genuinely think it's a great inroad to
| functional programming.
| automatic6131 wrote:
| The absolute state of github projects.
|
| This project should be exactly 1 (one) file. The readme.md.
|
| LICENSE - There is a license? Why? Someone might steal the text
| for their own blog post? So what? The license won't stop them.
|
| package.json - to install dozens of packages for... eslint. Just
| install globally. It's just markdown and code examples. Yarn.lock
| - ah yeah let's have this SINGLE, NON EXECUTABLE TEXT FILE be
| opinionated on the javascript package manager I use. Good stuff
| We have a .gitignore, just to hide the files eslint needs to
| execute. wow. FUNDING folder - wow we have an ecosystem of
| stating the funding methods?
|
| This should have never been a github repo. This is a blog post.
| It's a single, self contained post.
|
| I hate this crap. We have 9 files just to help 1 exist. It's
| aesthetically offensive.
| wtetzner wrote:
| Why do you care? Just link directly to the rendered markdown.
| Otek wrote:
| Wow, where should I start
|
| > LICENSE - There is a license? Why? Someone might steal the
| text for their own blog post? So what? The license won't stop
| them.
|
| But it's still good that author underlined that he don't want
| it to be copied. What's wrong with that?
|
| > package.json - to install dozens of packages for... eslint.
| Just install globally.
|
| Then other contributors won't know what version he used, what
| config he had, he won't be able to easily recreate it on
| different computer, etc...
|
| > Yarn.lock - ah yeah let's have this SINGLE, NON EXECUTABLE
| TEXT FILE be opinionated on the javascript package manager I
| use.
|
| That's author choice. Any good argument against it or you will
| just criticize for the sake of it?
|
| > This should have never been a github repo. This is a blog
| post. It's a single, self contained post.
|
| It's a blog post with 270 different revisions, 80 contributors
| and a bunch of different languages. Show me how to easily do
| that with a blog post.
|
| > I hate this crap. We have 9 files just to help 1 exist. It's
| aesthetically offensive
|
| Why number of files is offensive to you? We have a couple tools
| good at what they do to keep things consistent and organized.
| Better to have these tools to keep standards than not.
| jhrmnn wrote:
| The document appears to have 80 contributors, that's hard to do
| with a blog post. It could have been a wiki page. But then I'm
| not sure if hosting a single repo on Github is harder than
| hosting a wiki. And of course Github provides superior platform
| for collaboration compared to a wiki.
| seandoe wrote:
| I think it's cool that it's a repo. Now other people can submit
| pull requests and improve it. As for the files, bah whatever.
| Go find a squirrel to bark at.
| anewpersonality wrote:
| The ROI on learning FP is absurdly low.
| leetrout wrote:
| Are there a lot of people writing JS with leading semicolons like
| these examples?
| pacomerh wrote:
| I love these definitions and will bookmark. But there's a point
| where you can't really explain FP concepts in plain English
| right?.
| hoosieree wrote:
| They forgot "reason about" and "blazingly fast" which get thrown
| around all the time without ever being defined.
| aaaaaaaaaaab wrote:
| Constant Functor Object whose map doesn't transform
| the contents. See Functor Constant(1).map(n => n + 1)
|
| Ummm... how is this constant?
| debugnik wrote:
| It ignores the mapping completely:
| constant.map(...) == constant
|
| Constant functors are only ever useful if you need to thread a
| simple value through code that asks for an arbitrary functor.
| I'd say that's rare even for abstract, type-level heavy code.
| wodenokoto wrote:
| Not a bad list of definitions but definitely not plain English.
|
| It's quite technical even for experienced programmers.
| [deleted]
| nine_k wrote:
| It looks like the Partial Application section is missing the most
| widespread form of partial application, known as "creating an
| instance". class A: def foo(self, x):
| # do something a = A() foo(1) # self is already
| "applied".
| chongli wrote:
| That's a very good point. People think of functional
| programming languages and OOP languages as entirely separate
| worlds that, like oil and water, do not mix. In reality they're
| equivalent, they just have different ergonomics.
|
| For example, lambdas can be translated to anonymous inner
| classes.
| labrador wrote:
| When I was an assembly programmer, I knew C could help me
|
| When I was a C programmer, I knew OOP could help me
|
| When I was a JavaScript programmer, I knew TypeScript could help
| me.
|
| I don't know how functional programming can help me, but I'll
| keep trying to find a reason because people say it can
| jacquesm wrote:
| You can get a lot of mileage out of functional programming as a
| style without using a functional programming language, and you
| can do it in (almost) any other language, the big one: avoid
| side effects to functions as much as possible. That alone can
| make all the difference between a maintainable, refactorable
| and testable codebase compared to one that has side effects all
| over the place. If that's all you get out of it that's profit.
| labrador wrote:
| That's always been good programming practice to me, not
| functional per se. I learned the term 'spaghetti code' early
| on and took it to mean not just gotos, but lots of flags,
| especially globals.
| aeonik wrote:
| The biggest benefit I see is lack of side-effects. With a
| functional program you can be sure that your can safely call
| any function without having to worry about the current state of
| your app.
|
| A proper functional program can start to do very cool things
| safely: like hot reloading of code. When I'm debugging a
| Clojurescript app I can have a live running game, and update
| the physics without even reloading the page. It's all live.
|
| A proper functional program really looks like a series of
| mappings from a collection of data sources to a collection of
| data sinks.
|
| The keyword for this is referential transparency:
| https://www.braveclojure.com/functional-programming/
|
| There are other benefits like composability, designing your
| programs this way will give you access to algorithms that works
| otherwise not work with your code. The simplest example is Map,
| Filter, and Reduce. These functions are by their very nature
| parallel because a compiler knows that there are no
| intermediate steps, unlike a for loop.
| jacquesm wrote:
| Exactly.
| labrador wrote:
| I should probably add that I program mostly C# so I'm getting
| Functional benefits like Map and Filter because Eric Meijer
| added LINQ. He made it his life's work for a few years to
| bring functional programming to the masses.
|
| But I was minimizing state long before that because state
| makes any program much harder to understand.
|
| Confessions of a Used Programming Language Salesman: Getting
| the Masses Hooked on Haskell http://citeseerx.ist.psu.edu/vie
| wdoc/download?doi=10.1.1.72....
| Rumudiez wrote:
| C# might not be the best language to try FP in. F# is a
| popular alternative that might feel more familiar than e.g.
| Haskell. Since JS was one of my first languages, I learned
| a lot just from using the library RamdaJS which helped
| soften the learning curve for me
| singularity2001 wrote:
| Julia gives such beautiful examples for composabilty: Feed
| 3km+-10m into an algorithm which was written with just
| numbers in mind and units and confidence intervals often
| propagate all the way through.
| GnarfGnarf wrote:
| Can someone clarify something for me? If you build a linked list
| or a dynamically growing array in a Functional program, am I
| correct in understanding that the array is never modified,
| instead a copy is made and the new element is added to the copy
| of the array?
| olodus wrote:
| The result should be that after the addition you should not
| have affected the old array yes. The most basic way of
| implementing this is what you described. However, there are a
| number of different ways to optimize this. Since you know that
| the elements are immutable, if you add the new element to the
| start of the list you could just point it to the old elements
| and you now have two lists which share the majority of their
| elements. If you are interesting to learn about this more I
| would recommend looking into how for example Clojure implement
| their "persistent data structures". Most functional languages
| have similar things so you could find it elsewhere as well if
| you want.
| pdpi wrote:
| Immutable, fixed size arrays are pretty easy to handle in a
| functional style, of course, but most functional languages only
| discourage mutability while not outright forbidding it, so
| you'll usually have access to mutable dynamically growing
| arrays that work the exact same way as you expect them to.
|
| Because functional languages _do_ discourage mutation, they
| tend to fallback on an implementation of linked lists where the
| lists are never modified (You can google for Persistent Data
| Structures for more). Because the list never changes, you can
| have two lists share a tail, by building on two different heads
| atop that tail (like a git branch of sorts, minus the merging).
|
| If you want to build a pure language, you can build immutable
| dynamic-sizeable arrays with a copy-on-write setup. Growing the
| array keeps building on top of the existing buffer, if you
| outgrow it or need to actually mutate it, you make a copy.
|
| If you want both purity and actual full-on mutability, e.g.
| Haskell has Data.Array.MArray, which locks the array behind a
| mutable context (like IO), so your pure code never sees any
| mutation.
| Dagonfly wrote:
| Some helpful explanations in there!
|
| Though I think the closure example doesn't actually show
| capturing context. It's just a partial function application
| unless you count the literal '5' as a local variable.
| onlyfortoday2 wrote:
| eddyschai wrote:
| Obligatory "Hitler reacts to Functional Programming"
|
| https://www.youtube.com/watch?v=ADqLBc1vFwI
| synu wrote:
| It would be cool if this also told you why you might want to do
| the thing it's describing.
| TrackerFF wrote:
| While I appreciate FP using terminology from its math origins - I
| do think it's a huge barrier for entry, and not really sure
| languages that cling onto them, will see much real mainstream
| success.
|
| But then again, I don't think the language maintainers et. al.
| are too concerned with widespread success. Just some
| observations, but the majority of people I know that actively use
| FP languages, are academics. I've encountered some companies that
| have actively gone with a FP language for their main one - but
| some have reverted, I guess due to the difficulty of hiring.
|
| With that said - functional elements are becoming more common in
| widespread languages, but not all the way.
| dkarl wrote:
| I think using math terminology is honest, in that it gives an
| accurate expectation of how the ideas can be communicated and
| learned. They are simple ideas that can be communicated in
| their entirety in just a few symbols, but it takes time,
| exposure, and practice to develop facility in their use and a
| feeling of "understanding."
|
| That's the math experience. I know people hate that and would
| much rather it be a matter of reading some a nice explanation
| and then "aha" but there's no such explanation yet and after
| years of people trying to develop one there's no point in
| expecting one right around the corner.
| ebingdom wrote:
| I agree about the math terminology, but I think it would be
| more confusing if we created a completely different set of
| vocabulary for the same concepts. So I don't really know what
| to do: refer to something by its proper name, or create a new,
| less precise name to make it sound less scary? Why do we find
| certain identifiers scary in the first place?
| ncmncm wrote:
| A "functional language", like an "object-oriented language", is
| an exercise in futility. But support for a functional approach
| in a general language will often be useful.
| ribit wrote:
| Now can someone do the same for web dev? I just started getting
| into it for a hobby project and the terminology is so incredibly
| idiosyncratic.
| ijidak wrote:
| This is great. Finally understand monads a little better.
|
| Definitely saving this for later.
| ratww wrote:
| Btw, that "chain" function is available natively in Javascript,
| as "flatMap". Monads aren't as mysterious as we make them look.
| jstx1 wrote:
| Why do monads come up so often when people talk about FP? Is it
| a meme or are they really an important and difficult to
| understand concept?
| pdpi wrote:
| If you want a lazy-by-default language, you need to deal with
| a problem -- laziness means you don't need to actually
| evaluate the reads until you use `a` and `b`, and the print
| uses `b` before `a`, so the two reads can be executed in
| reverse order: a = read() b =
| read() print("{b}, {a}")
|
| One of Haskell's original goals was precisely to be lazy-by-
| default, which necessitated coming up with a way to solve
| this problem, and monads are the solution they came up with
| that gave us reasonable ergonomics. From a practical point of
| view, monads are just types that have reasonable
| implementations for three simple functions: `pure`, `map`,
| and `flatten` # lists as monads:
| pure 1 = [1] # put a value "inside" the monad map f,
| [1, 2, 3] = [f(1), f(2), f(3)] # apply the function to the
| "inside" of the monad flatten [[1], [2, 3]] = [1,2,3]
| # take two "layers" and squish them into one
| # also, the simplest, but least useful, way to use functions
| as monads: pure 1 = (x -> 1) # putting a value inside
| a function is just giving you the constant function
| map g, f = (x -> g(f(x))) # map is just composition
| flatten f = (x -> f(x)(x)) # you squish by returning a new
| function that performs two nested calls
|
| ("reasonable" here largely means "they follow the principle
| of least surprise in a formal sense")
|
| The trick is that, once you know what monads are, you can use
| them in any language (with varying degrees of support), and
| you can see instances of them everywhere, and it's an
| incredibly useful abstraction. Many common patterns, (like
| appending to a log, reading config, managing state, error
| handling) can be understood as monads, and compose quite
| well, so your program becomes one somewhat-complex data type,
| a handful of somewhat-complex functions that build an
| abstraction around that data type, and then lots of really
| small, really simple functions that just touch that
| abstraction. I have a .class parser written in Scala that
| exemplifies this general structure, need to put it up
| somewhere public.
| ufo wrote:
| I think one of the reasons for the meme is because there's so
| many monad tutorials. When a Haskeller is introduced to
| monads they'll run across all these monad tutorials with
| abstruse analogies for what a monad is. Is a monad a burrito?
| Or a space suit? Odds are that none of these analogies will
| make much sense and the programmer will have to figure out on
| their own, what is the deal with monads after all. At some
| point they might have an epiphany; monads are the sort of
| idea that is actually pretty neat when it "clicks". They will
| feel compelled to write a monad tutorial, and thus history
| repeats itself.
| lopatin wrote:
| They come up because people who don't understand them think
| they are important and people who do understand them want
| other people to know.
|
| Also, speaking of memes:
| https://www.youtube.com/watch?v=ADqLBc1vFwI
| AtNightWeCode wrote:
| I think it is because monads can be used for handling side
| effects, even though side effects do not exist in FP. :)
| imtringued wrote:
| public static void main(String[] args)
|
| Why does this come up so often when people talk about Java?
|
| Because beginners are confronted with the IO Monad if they
| want to write a Hello world program.
|
| Monad is a typeclass that any datatype can implement. Monads
| have a then or flatmap like function that takes a Monad and a
| function that operates on the contents of the monad but also
| returns another monad of the same type which is then combined
| according to the implementation details of the specific monad
| that implements the monad typeclass.
| frogulis wrote:
| Many important "effects" (e.g. non-determinism, IO,
| asynchrony, environmental context, state... etc) are modelled
| as monads in Haskell.
|
| You _can_ write Haskell code without understanding what a
| monad is, but composing and creating these things is going to
| be a little painful without that understanding.
|
| Additionally, it seems to be a harder concept to grasp than
| e.g. functors or monoids.
|
| I think this can be partly attributed to many Haskell
| programmers first being introduced to monads that are less
| than ideal for understanding the concept.
|
| Shameful plug, I've written some thoughts on this here:
| https://frogulis.net/writing/async-monad
| tymscar wrote:
| I think this was some amazing insight. I understand monads
| and I can use/write them but I feel like the concept hasn't
| FULLY clicked yet. While even now it hasn't, I feel like
| your blogpost got me that step closer to it, so thank you!
| lucasdicioccio wrote:
| There is a myth that "monads" are magical insights of some
| sort -- it's not.
|
| Difficult to understand: likely yes because the myth is not
| groundless. What "monads" capture is how to combine things
| with a lot of ceremony: (0) the things that we want to
| combine are sharing some structure/properies (1) we can
| inspect the first thing before deciding what the second thing
| is (2) we can inspect both before deciding what is the
| resulting combination. What requires a lot of thought is
| appreciating why "inspect, decide, combine" are unified in a
| single concept.
|
| Important: indeed, because in Haskell-like languages monads
| are pervasive and even have syntactic primitives. It's also
| extremely useful when manipulating concepts or approaching
| libraries that implement some monadic behaviour (e.g.
| promises in JS) because the "mental model" is rigorous. If
| you tell someone a library is a monadic-DSL to express
| business rules in a specific domain, you're giving them a
| headstart.
|
| Some final lament: there's a fraction of people who found
| that disparaging (or over-hyping) the concept was a sure-fire
| way to yield social gain. Thus, when learning the concept of
| monads, one situational difficulty that we should not
| understate is that one has to overcome the peer-pressure from
| their circle of colleagues/friends. Forging one's
| understanding and opinions takes more detachment than the
| typical tech job provides.
| xigoi wrote:
| Haskell uses monads as an escape hatch for performing side
| effects, so they come up often there.
| endgame wrote:
| Not as an "escape hatch" (that would be something more like
| `unsafePerformIO :: IO a -> a`), but as a principled way to
| compose (among other things) IO actions.
|
| A Haskell program executes the IO action at `Main.main`,
| which must have type `IO ()`.
|
| `putStrLn :: String -> IO ()` is a pure function - if you
| give it the same input, it always the same IO action as a
| result.
| ogogmad wrote:
| Monads are (for better or worse) contagious. So when one
| function calls a monad, then it needs to be included in
| the monad as well. It makes introducing memoisation to a
| file, randomisation, and memoisation not-to-a-file
| (without using lazy evaluation to express it) difficult.
| I don't know whether the alternatives (effect systems for
| instance) help with this.
|
| I personally don't use functional languages because I
| find them too difficult given the needs and interests I
| have. I think about computations sequentially most of the
| time.
| ratww wrote:
| Monads themselves aren't really contagious, it's the
| actions that would otherwise have side-effects that are,
| and also the fact they have to be executed in sequence.
|
| This is also true for other things in other paradigms,
| such as async functions in javascript.
|
| This is a good thing, however. In imperative programming,
| you have invisible temporal coupling. In pure-FP you have
| the same coupling, but it's exposed.
| garethrowlands wrote:
| While I broadly agree with you, I think the post you're
| responding to has a point. You can't, in general, get a
| value back out of a monad, so if you call a monadic
| function, you may well have to return a monad. The
| obvious example is IO: there's no (safe) way get the `a`
| from `IO a`, so IO is kinda contagious.
|
| Then again, there are lots of monads, such as `Maybe` and
| `List`, where you _can_ get values out. These aren 't
| contagious at all.
|
| I agree with you that this is a good thing. Effects show
| up in the type signature - and it's all about those
| effects and managing them.
| ratww wrote:
| Yes, this is what I mean. IO is contagious for reasons
| unrelated to it being a Monad.
| endgame wrote:
| I think that applicatives and monads feel more contagious
| than they really are, because at first people tend to
| write functions that consume values of type `f a` too
| readily. This is because it takes some time to become
| comfortable with `fmap` and friends, so the new Haskell
| programmer often doesn't write as many pure functions.
| fhars wrote:
| But then monads _are_ a way to think about computations
| sequentially. If I write highly sequential code in a
| C-like language, in many cases most of the code is just
| boilerplate made necessary by the absence of native
| support for monads: int ret =
| doStepOne(); if (ret == RESULT_OK) {
| ret = doStepTwo(); } if (ret ==
| RESULT_OK) { ret = doStepThree(); }
| return ret;
|
| would just be doStepOne() >>=
| doStepTwo() >>= doStepThree()
|
| in a language with support for monads.
| alpaca128 wrote:
| I'd say both.
|
| For me the difficulty comes from the formal
| explanations/definitions, those always manage to confuse me.
| Result & Option types seem to have something to do with it so
| I may already have some understanding of the concept. But
| many explanations containing the word Monad also contain
| various other abstract mathematical terms. Trying to explain
| the concept to someone without mathematical background can be
| tricky.
| goto11 wrote:
| They are used pervasively in Haskell, less so in other
| functional languages.
|
| In Haskell, you can't write a "Hello world" program without
| using monads, so you cant really avoid learning about them.
|
| IMHO monads are only really useful in Haskell because it has
| specific built-in syntax sugar to support them. Without this
| syntax sugar, they would be very cumbersome to use. So it's
| not really the monad type per se which is interesting, it is
| the code style which the syntax sugar enables.
| IAmYourDensity wrote:
| And the reason you can't write "Hello world" in Haskell
| without using a monad is that functions in Haskell are
| "pure", meaning they cannot have side effects like
| outputting to the console.
|
| Preventing side effects, including reading and writing
| global state, helps prevent bugs and makes it easier to
| understand and refactor Haskell code. Some would argue that
| the extra layers of abstraction from category theory and
| unpredictable order and number of lazy evaluations can
| actually make it harder to understand and refactor Haskell
| code.
|
| Anyway, in order to perform I/O in Haskell, you evaluate
| your pure functions as a sequence of actions that are
| executed by the Haskell runtime. The construct that helps
| you build the sequence of I/O actions and allows you to
| bind their intermediate values to arguments to be used by
| subsequent actions is called the 'IO' monad.
| garethrowlands wrote:
| While it's true that `IO` in Haskell has a `Monad`
| instance, you don't really have to know that to do `IO` in
| Haskell. Certainly you don't need to know `Monad` in the
| abstract to use `IO` concretely.
|
| I like Haskell's `do` notation, which is its syntax sugar
| for monads, but it's really not _that_ bad without. For
| example: do name <- getLine
| putStrLn ("Hello " ++ name)
|
| isn't really _that_ much nicer than:
| getLine >>= \name-> putStrLn ("Hello " ++ name)
|
| or even: getLine >>= \name->
| putStrLn ("Hello " ++ name)
|
| The main reason that monads are important in Haskell is
| that programs that do IO simply are not functions in a
| mathematical sense. If Haskell were limited to functions,
| it wouldn't be able to do IO.
| pseudosavant wrote:
| I didn't really understand FP until I read Functional Light
| JavaScript by getify/Kyle Simpson. It is so well written and
| approachable by mere mortal coders. I'm not an FP wizard, but it
| is the coding paradigm I mostly use now. I've even adapted some
| aspects (e.g. composability) to the CSS I write using `var()`.
|
| You can buy the book or read it on GitHub:
|
| http://fljsbook.com/
|
| https://github.com/getify/functional-light-js
___________________________________________________________________
(page generated 2022-07-04 23:01 UTC)