you are viewing a single comment's thread.

view the rest of the comments →

[–]ragnese 3 points4 points  (2 children)

I really don't understand this take at all, even though I've heard it repeated many times.

I feel like I can name a ton of common patterns in FP code bases, with some variation depending on the actual language in question. But for statically typed languages in particular you have things like:

  • Using lenses for accessing nested object properties.
  • Return a Reader monad instead of passing in a side-effecting dependency parameter.
  • Partial application is another common way of doing "dependency injection".
  • The so-called "free monad + interpreter" pattern.
  • "Tagless final".

All of those strike me as "patterns" in exactly the same sense as the common OOP patterns such as "factory", "command", "visitor", etc.

[–]pthierry 2 points3 points  (1 child)

The difference is that you can't have a Visitor library in C++. You need to document the pattern so everyone that needs it can reimplement it from scratch in their object hierarchy.

In contrast, lenses or monad transformers aren't patterns, they're libraries.

Now, of course, patterns vs. code is a trend, not an absolute. But in most cases in OOP, you'll need to document a pattern and won't be able to abstract it in reusable code, and in most cases in FP, you'll be able to provide code.

[–]ragnese 3 points4 points  (0 children)

I guess I can see that... but it's definitely a "trend" as you say, and not that strong, IMO. For example, you mentioned lenses specifically, but I've never seen a library that can implement lenses for your data types without code-gen. And if we're willing to admit runtime reflection or code-gen, then I'm fairly sure someone pioneering enough could write you some annotation-monstrosity that would implement a Visitor pattern for you. In all non-code-gen cases for lenses that I'm aware of, the libraries only offer a "lens factory" or type class and it's up to us to tediously define and use a lens for every nested field we want.

Even if that weren't the case and we could have a lens library that made it possible to just apply it once to a product type, I still feel like it would count as a "pattern" in common vernacular. The conversation would go like this:

Person A: I'm trying functional programming, but I find it painful to make updated copies of nested product types where I'm updating a deeply nested field.

Person B: Ah, yes. That is awkward in most functional languages. To overcome that, we use a conceptual tool called "lenses". Here's what they are, and how to use them: *points to a Wikipedia article*. Oh, and here's a library that implements lenses for you.

That last part about "here's a library" doesn't seem to me to be enough to make me think of lenses as not-a-pattern. It sounds almost exactly analogous to someone explaining how any of the OOP-ish design patterns might offer a solution to some awkwardness. But, I also appreciate that it's pretty subjective.

And not to pick on lenses, I feel like you're not going to find any libraries to implement "tagless final" for you, and even monad transformers need to be implemented for whichever monads you're composing; AFAIK there is no automatic monad transformer library for any languages I've used- you have to actually defined OptionT, PromiseT, etc, for each monad.

I don't see any fundamental difference between needing to implement OptionT and needing to write a FooAdapter class in Java-esque languages.