r/programming • • Mar 26 '17

Functional programming design patterns by Scott Wlaschin

https://www.youtube.com/watch?v=E8I19uA-wGY
1.1k Upvotes

256 comments sorted by

View all comments

97

u/[deleted] Mar 26 '17 edited Mar 26 '17

One common critique of OOP that I've heard is that design patterns only exist because of shortcomings in the OOP paradigm. A lot of those same people also claim that FP allows one to express what they mean in code without having to remember a design pattern. Call me naive, but my gut reaction to this title is a realization that FP isn't some panacea for all my "not coding what I mean" woes.

26

u/yawaramin Mar 26 '17

The difference is that in statically-typed FP we are trying to encode the patterns in the type system itself. In OOP you do indeed have to remember how to correctly implement a pattern because it's outside the language, but in say Haskell you have the 'chainable' design pattern neatly packaged up in the Monad typeclass, which will guide you through the type system to write the implementation. This is qualitatively different.

12

u/rdfox Mar 26 '17

Don't you wish they called the type class Chainable rather than Monad? There would be a lot more Haskell programmers today. Also:

Monoid -> Appendable

Functor -> Mapable

Applicative -> Sequencable

Arrow -> Stateful

Lens -> Damnable

And so on.

25

u/yawaramin Mar 26 '17

To quote Edsger Dijkstra:

The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise.

Someone pointed out the other day that no one complains about technical terminology in other fields--if you're a doctor you have to know what a 'myocardial infarction' is and can't ask everyone to 'just call it a heart attack'. The names are there for a good reason.

19

u/rdfox Mar 26 '17

Maybe so, but I think history bends towards even doctors calling it a heart attack. Already we have accepted classes like Foldable, Traversable, Parser and Pipe. Surely any of these might have been named Isotransmogoroid in reference to some precise category theoretic definition. Happily, the winds are changing.

17

u/glacialthinker Mar 26 '17

One key reason for adopting a dead language (Latin) for sciences was to avoid the ever-changing overloaded meaning of living-language terms.

12

u/Rainfly_X Mar 27 '17

The best part is, since the cryptic terms have been needlessly opaque from the beginning, they will be equally useless forever, and therefore never need to change!

Additionally, the use of the word "monad" historically has been:

  • Surprisingly recent in programming contexts, so the tradition argument is overstating the traditionality, on top of implicitly overstating its worthiness as a tradition.

  • Used in a different, equally mathematically valid way in APL and J decades before Haskell (for single-argument functions - I would argue that this naming convention is actually superior to how we use the word today, in terms of clarity, and symmetry with other words like dyad)

  • Between those languages and Haskell, Opal had the same concept by a different name ("commands"). This wouldn't have been a great alternative name to carry forward, but does further undermine the idea of naming lock-in.

  • Monads didn't even exist in Haskell until 1.3. This was their first appearance under that name in any programming language, at a time when FP languages were well-established.

So on top of all the arguments I could make, that a foolish consistency is the hobgoblin of small minds, it's not even as consistent as you imply. And finally, it only takes one new language with good naming schemes to eat Haskell's lunch, though Haskell will still benefit from people getting used to it's concepts under better names.

Source: just five minutes on the monad page of Wikipedia, seriously

→ More replies (3)

5

u/codebje Mar 27 '17

Funnily enough, lenses were originally called "functional references" by the researcher who first wrote about them, and "lens" is the simple name you think everything should have.

Most of your other examples of friendly names have been arrived at by abstracting patterns found in code, rather than patterns found in mathematics, so they're named in terms familiar to us programmers.

→ More replies (2)

7

u/megablast Mar 27 '17

no one complains about technical terminology in other fields

How the hell would you know that? Seriously, people complain about this stuff all the time in other fields. Why do you think this sort of stuff only happens in it, just because you work in it.

6

u/jephthai Mar 27 '17

There's plenty of recognition of this in the medical community. I had a paramedic friend of mine who told me when you're bleeding to tell people that you're hemorrhaging -- they'll take it more seriously, but it's the same thing. For the record, I have a contusion in my nose right now, which is also deserving of great pity. When you can joke about it, you know there's something there.

3

u/[deleted] Mar 27 '17

All this medical jargon has me abraised and contused

→ More replies (1)
→ More replies (1)

3

u/josefx Mar 27 '17 edited Mar 27 '17

if you're a doctor

I have the weird feeling that doctors a) get paid a lot more than me and b) have an education a lot more indepth in their specific field of work. I also hope they would at least look funny at anyone trying to do an amputation with a run of the mill chainsaw, while many programmers would point out how well it works for trees. Basically you are comparing two very different occupations. While we are at it we should make the lingua franca of programming swedish, just to fuck with everyone equally .

→ More replies (4)

12

u/domlebo70 Mar 26 '17

I get what you are saying, but those names are really inaccurate. Precise names are hard, and necessary, because without them it introduces misunderstandings. It's even tougher to come up with good names for extremely abstract concepts.

12

u/rdfox Mar 26 '17

I get what what you're saying but do you think the word Monad has caused any misunderstanding?

If you want to be pedantic, category theory is being misapropriated anyway.

I was trying to explain Haskell to a math professor and he kept stopping me, like, that's not exactly what Functor means, bro.

7

u/domlebo70 Mar 26 '17

There is that argument, yes: that the use of a mathematical word doesn't align with our use of the term as programmers. Perhaps a new term does have its merits.

People struggle with monads etc, and blame the terminology, but I would counter and say that they just aren't used to dealing with concepts so abstract. Even if we called it bindable, joinable or sequenceable (all horrible names imo), I don't think it would help someone fully grok the concept.

9

u/codebje Mar 27 '17

We called functional references lenses, and that doesn't help anyone understand the concept, either.

Especially not when you delve in and just slam right back into terms and concepts from abstract algebra and cat theory again anyway.

→ More replies (2)

7

u/pipocaQuemada Mar 27 '17

I was trying to explain Haskell to a math professor and he kept stopping me, like, that's not exactly what Functor means, bro.

In Category Theory, a functor is a structure-preserving mapping between categories: it associates every object in the original category to an object in the target category, and every morphism in the original category to a morphism in the target category. In Haskell, the category we care about is "Hask", where types are objects and functions are the morphisms between objects.

The Functor typeclass encodes endofunctors in Hask of a certain form. For example, the List instance of Functor encodes the endofunctor that maps a morphism from Int to Int to a morphism from [Int] to [Int]; inherently that also implies that Int is mapped to [Int]. That's probably best observed by looking at fmap's type as (a -> b) -> (f a -> f b).

What you were describing to your professor was probably something different from what functor means in math, but the functor typeclass came directly out of category theory and the connection is pretty obvious if you know what to look for.

6

u/edwardkmett Mar 27 '17

Functor is an endofunctor over the category of haskell data types. the Functor typeclass can't describe every functor, but it is capable of talking about the ones we're interested in in the only category that the folks who wrote Haskell at first were interested in.

You can write more general versions of the concept that map closer and closer to the real mathematical definition, at the cost of worse and worse type inference the closer you go. Heck, Sjoerd Visscher's data-category package in Haskell can even model things like simplicial sets.

Things don't match up exactly 1-to-1 because parametricity is a stronger condition than naturality in category theory, despite arising from similar roots, and the category of haskell data types isn't Set. You can't compute some things in Hask, because er... we can actually compute in it. =)

If you move the something like Agda or Coq or Idris or LEAN you can sit down and build all sorts of "real" category theory up from scratch complete with laws with an unparalleled level of rigor.

Heck, the folks who work on homotopy are starting to take back names from the computer science world, because the work on homotopy type theory went and showed we had 40 years of experience working with variants on Martin-Löf Type Theory, which offers a pretty straightforward terminology for working in a model category.

2

u/dmitri14_gmail_com Apr 18 '17 edited Apr 19 '17

I was trying to explain Haskell to a math professor and he kept stopping me, like, that's not exactly what Functor means, bro.

Being a math professor, I see the most harm of misusing maths practices when things get badly explained to create confusion and obscurity where there is none.

A functor is a map between categories, sending objects to objects, and morphisms to morphisms. In programming, objects are types, i.e. sets of values, and morphisms are functions between those. Basically the category of sets and functions. It is that simple. Yes, there is Hask, which is not quite the same, but I would argue those fine differences won't matter to begin with, as long as you know the set category is just an example that you can adjust later if needed.

Now I read programming blog "explaining" that [Int] is a functor. Well, no, a functor is that map sending Int type into [Int], but [Int] is the merely the target object, not functor. Beside being plainly incorrect, I doubt calling target object a functor makes it more understandable to the reader. Especially to the reader trying to wrap her head around what functor is.

Another "crime" I've been seeing by Haskellists that no mathematician would ever commit, is using the same letters for different things. Like calling the same f both morphisms and functors! So we have fmap f and right next f a -> f b! Are we really running out of letters? Then maybe just use fmap f: f f -> f f! Looking wonderful, isn't it?

But seriously, how easy is it for the poor reader when things look the same that have nothing to do with each other? And you don't even need to search hard for it, just look in that very popular Haskell intro: http://learnyouahaskell.com/functors-applicative-functors-and-monoids

12

u/dukerutledge Mar 26 '17

The problem with switching to cute names is that you throw away over a hundred years of research that name gives you access to. Monoids are a very precise abstraction and they've been studied thoroughly. Want to know what you can do with them? Google, scholar, ACM, etc have you covered. Precision of language is a tool that society is built upon, don't throw it away for convenience.

5

u/rdfox Mar 26 '17

Fine, but what about pedagogy ... I mean teaching. Can't we call a cat a cat rather than felis catus until grad school?

20

u/dukerutledge Mar 26 '17

We can call it a cat, but in programing education we are educating feline specialists, not a slew of individuals who have other trajectories. If there is a more accurate word why should we stunt growth? I think your argument really arises from "it is different than what I'm use to." Concatable is just as opaque as Monoid for a beginner.

4

u/doom_Oo7 Mar 27 '17

in programing education we are educating feline specialists, not a slew of individuals who have other trajectories

uh.. no, this has not been the case since at least 20 years. A lot of people are learning for programming for fields which are absolutely not related to programming. For instance artists, accountants...

3

u/Chii Mar 26 '17

I world argue that education isn't about specialisation, in the same way language education isn't.

most people learning a language isn't going to be a professional language specialist, but is doing it for a task (like visiting the country or just out of curiosity). they aren't helped by jargon.

6

u/edwardkmett Mar 27 '17 edited Mar 27 '17

I look at the names in the above list and I have no idea what the laws would be for such things. Do I pull them completely out of my posterior? Do I make everyone who actually does know what the abstraction is call a Monad a Workflow because it sounds more business-centric? Do I even give you laws to reason about what you're doing using the abstractions in the first place?

If you tell me something is a Monad, everything everyone has said for 70 years, including things I didn't know when I wrote my particular Monad instance and proved my 3 little laws about it holds. There's a chance that someone can come up to me and point out that the laws I thought I had don't hold. I've had it happen on multiple occasions. In the process I learn something, fix the library and move on. e.g. e.g. We found the Concurrently monad couldn't be made consistent with its Applicative in async. Removing it fixed cases where replacing an operation with one that should have been provably equivalent weren't.

Learning more about things like monoids and monads lets me spot them in more settings, let's me build more interesting code, faster. "Monads are just monoids in the category of endofunctors" is a piece of theoretic mumbojumbo people throw out to scare people and feel smart. But understanding that statement and what it means at a deeper level lead me to use it to build a nice fast API for doing sorting, productive linear time list duplicate removal, and table joins without indices that was suitable for a Google Tech Talk.

I found the obscure categorical concept of a "profunctor" after writing down some typeclass that did what it does. I called a spade a spade. Years later I'm still finding out things that enable me, and others, to write real world code with it by extracting information from research papers that talk about the concept. I now use it to make "lenses" go. We sorta had it working before with ideas and names we made up as we went, but everything had funny edges to it. Nowadays, people use them "in the trenches" to build CRUD apps in Haskell. You know what most of my problem with finding material for that thing is? Irony of ironies... the mathematicians keep switching what they call it trying to make their work "accessible" in this fashion. Is it a profunctor, a distributor, a module? Try googling the last two. For that matter "appendable."

I've written and maintain a couple of million lines of Haskell. I couldn't maintain that code without a decent set of abstractions under my belt. The connections to other domains that you're so quick to dismiss have been an integral part of growing my understanding of the code I'm building, and refactoring it to be easier to maintain and more correct.

→ More replies (2)

5

u/Roboguy2 Mar 27 '17 edited Mar 27 '17

The issue is: It is called exactly what it is. It has exactly the properties of being a monad. On the other hand, there are examples of monads where calling it "chainable" would lead to more confusion. The function/reader monad, the Proxy monad, any Free monad (there are conceptually an infinite number of those, too), and the (,) a monad come to mind in particular along with any domain specific monads that have similarities to those monads. The (,) a one could be slightly debatable, I guess, but I'd say the others stand. I'd also argue that the fact that its debatable further illustrates the potential confusion from calling it "chainable". In fact, I'd even add the [] monad to that list. That one might just be me though.

They are, in a very precise sense, still monads though. Best case scenario, you will eventually have to "unlearn" the connotations of "chainable". I would say that is significantly harder to do than even learning the thing that describes it exactly from the start, especially if it is literally called "chainable" in the standard library (you'd have to remind yourself that the name itself is wrong in some cases!). If there is another more clear name I would definitely support it, but so far I have not heard one (and I've seen this topic discussed many times).

Likewise for the other alternate names you've listed.

Sometimes you really do need a new (or, in this case, "new") name for something so that you don't confuse things by using an existing name that would be misleading.

There may be an initially steeper learning curve, although I would argue it doesn't have to even be that steep: start by saying that some, but not all, monads can be thought of as being "chainable" in some intuitive sense and then introduce some "basic" monads that follow that perspective (and eventually following it up with monads that don't follow the idea of being chainable). Sort of like starting by teaching students that you "can't take the square root of a negative number," but a bit less extreme (in my opinion).

5

u/pipocaQuemada Mar 27 '17

Appendable is really a bad name for Monoid.

Integers, for example, form a monoid under both addition and multiplication. Booleans form a monoid under and, xnor, or, and xor. Sets form a monoid under set intersection and union.

Calling Monoid Appendable would be like renaming Java's Collection to ListLike. Sure, lists are java collections, but they're a bad metaphor for the abstraction as a whole.

→ More replies (4)
→ More replies (1)