
Why OO Matters in F# - douche
https://eiriktsarpalis.wordpress.com/2017/03/20/why-oo-matters-in-f/
======
bad_user
I have recently given a talk that includes a comparison between OOP and
parametric / higher-kinded polymorphism, the hope being to help people choose
the proper abstractions in a hybrid language like Scala.

Title is "Functional Programming Inception", slides available at:
[https://alexn.org/blog/2017/03/15/fp-
inception.html](https://alexn.org/blog/2017/03/15/fp-inception.html)

Basically:

\- OOP is about _information hiding_ and not just when speaking of data, but
also when speaking of types, so OOP is about hiding information at the type
level. Thus OOP is also really good at heterogeneity, Liskov substitution
principle and all that

\- Parametric polymorphism is compile time, nothing gets deferred, types are
very explicit and higher kinded polymorphism is about _composition_ , with
plugged-in types altering the capabilities of built instances dramatically ...
in other words, it's a very different beast

In regards to F#, the problem is that in absence of OCaml functors and Haskell
type-classes, plus due to lacking higher-kinded types, you're left with OOP
for building abstractions. And in terms of OOP, without interfaces that can
have default implementations or higher kinded types for that matter, it's not
that good as an OOP language either.

~~~
Tarean
An insight I found quite interesting:

oo style is great at extending types. You can add a subtype at a later date
without modifying existing code and it just works.

Functional style is great at extending behavior. You can take an existing type
and implement new functions on it. This means you can have a lot of functions
defined on a basic data type which is amazing for composition but you can't
add a new type that works with the same methods if you didn't anticipate this
need.

~~~
tjalfi
This is called the expression problem.

[http://homepages.inf.ed.ac.uk/wadler/papers/expression/expre...](http://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt)

[http://stackoverflow.com/a/2079678](http://stackoverflow.com/a/2079678) is a
great SO answer on this topic.

~~~
pron
The expression problem has a complete and rather elegant solution in
mainstream OO languages with generics, invented in 2012 called object
algebras:

Video: [https://www.infoq.com/presentations/object-
algebras](https://www.infoq.com/presentations/object-algebras)

Paper:
[https://www.cs.utexas.edu/~wcook/Drafts/2012/ecoop2012.pdf](https://www.cs.utexas.edu/~wcook/Drafts/2012/ecoop2012.pdf)

I understand that there is a comparable solution in FP languages called
finally tagless interpreters:
[https://oleksandrmanzyuk.wordpress.com/2014/06/18/from-
objec...](https://oleksandrmanzyuk.wordpress.com/2014/06/18/from-object-
algebras-to-finally-tagless-interpreters-2/)

~~~
kvb
Vesa Karvonen posted a complete solution in SML here[1] (but LtU seems to be
down so the link doesn't work), which I transliterated (directly, not
idiomatically) to F# here[2].

[1] [http://lambda-the-ultimate.org/node/2232#comment-31278](http://lambda-
the-ultimate.org/node/2232#comment-31278)

[2] [http://fssnip.net/gn](http://fssnip.net/gn)

------
osd
Enjoyed the article. My personal battle with f# has always been searching for
what is idiomatic. Everything feels a little bit foreign when you write
something in f#. One of the responses the author leaves in reply to a comment
is something that I found important the more I used f#, and is something that
never really gets said out loud.

"Bottom line: people need to embrace the fact that F# is a hybrid language,
and that the primary motivation for using F# is the .NET platform. People
interested in FP but not in .NET should just try SML or Haskell. Claiming that
F# is “up there” with Haskell or SML is false advertising and ultimately deals
unpleasant surprises to people coming either from .NET or Haskell
backgrounds."

f# needs to find its place not only in the FP world, but also in the .NET
world. That's a very hard thing to do. The language is fun, and I hope it
finds its way.

~~~
maxxxxx
I tried to learn FP by doing a project with F# but I felt like I was writing
C# code with a different syntax. I would like to give FP another go with a
purely functional language but then you also need libraries that enforce that
approach. The .Net framework will always lead you to an OO approach.

~~~
kemiller2002
What I found is that it took me about three tries to actually grasp functional
programming. My first F# project started out the same way yours did. It was
really OO in an F#. Once I looked at it, I started to realize that it was not
the language, but my approach and changed it to fit the FP approach.

------
romaniv
I've been thinking and reading about OOP and its roots recently.

Here are some interesting resources on the subject:

Alan Kay's elaboration on what he envisioned when he coined the term:

[http://userpage.fu-
berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...](http://userpage.fu-
berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en)

Two papers he references:

[https://www.rand.org/content/dam/rand/pubs/research_memorand...](https://www.rand.org/content/dam/rand/pubs/research_memoranda/2007/RM5290.pdf)

[http://repository.cmu.edu/cgi/viewcontent.cgi?article=2281&c...](http://repository.cmu.edu/cgi/viewcontent.cgi?article=2281&context=compsci)

Cool article on failures of OOP:

[http://www.smashcompany.com/technology/object-oriented-
progr...](http://www.smashcompany.com/technology/object-oriented-programming-
is-an-expensive-disaster-which-must-end)

Its long and frustrated, but most of the points he makes are true. A lot of
the stuff people claim as

I think the original vision of OOP is more valuable than what we call OOP
right now. I think a good balance would be writing low-level stuff in
functional style and then wrapping it in objects when you need state and
cross-library messaging. Also, it seems there is some overlap between the
original vision for OOP and what we call "microservices" today.

------
saosebastiao
Mirror of my question from Reddit:

Don't classes cover a full superset of the functionality of ML-style functors?
I have yet to find a functor-dependent construct that can't be mimicked with
them, and I've found plenty of use cases of classes that can't be mimicked (or
at least not without hacky workarounds) with functors. And yet it seems common
for FP style programmers to lament the lack of functors in F#, but will
consider it a codesmell to use classes. Am I missing something here? What does
a functor do that is so taboo to do with classes?

~~~
Tarean
You might be misunderstanding functors since they are basically tangential to
classes. Every generic class `F<T>` where we can lift functions `T -> S` to
`F<T> -> F<S>` is a functor.

Think of lists as example, they are generic over a type and we can apply
functions over the base type by applying them to each element.

Functors are a basic building block of category theory. Category theory is
basically the essence of composition so using functors leads to easy to reason
about and extremely composable code. This also extends to things that build on
functors like monads.

~~~
pseudonom-
ML uses "functors" in a different sense than e.g. Haskell.

------
Ace17
Whatever we call it, e.g "OO", inversion of control, dependency injection,
polymorphism, function pointers, ...

This can all be summarized in one question: "can old code call new code?".

This is required to build a proper modular architecture (and BTW this paradigm
is used by lots of C projects like the Linux kernel, VLC, ffmpeg, GPAC).

This doesn't go against the fundamental principles of FP ; as long as the type
system of your language doesn't need to know "all" types (i.e including the
ones you're going to your project later).

~~~
jdmichal
This is why I have generally moved to contract-oriented programming with
dependency injection. In C# and Java this manifests as interfaces. In Rust
they would be traits. I believe Haskell type classes would serve the same
function. So, you can call new code from old code fine, as long as your new
code implements the contract.

This was really based on the realization that the Liskov substitution
principle effectively mandates that every object type is an implicit contract.
Not breaking that implicit contract in polymorphic code is what LSP is about.
So then the question becomes, why not just use the construct that directly
corresponds to a contract instead? That way you can reserve polymorphic
constructs for actual identity (IS-A) relationships and cleanly maintain SOLID
principles.

EDIT: Reading a bit further down to romaniv's comment [0], I think this is
possibly what Kay discusses as the ADT approach. I've never heard of that
before, but based on the Wikipedia article it seems to fit the bill?

[0]
[https://news.ycombinator.com/item?id=13931826](https://news.ycombinator.com/item?id=13931826)

------
ungzd
OO in F# looks like fusion of multiple concepts into one:

\- Ad-hoc polymorphism

\- Infix function application syntax (foo.append bar instead of append foo
bar)

\- Product types with named members, records (class members, note that
language already has records)

\- C# FFI

So I'm not a fan of this "multiple responsibility" approach. For example, you
can't use ad-hoc polymorphism unbundled from OOP classes. You can't use
records with OOP because it's not bundled into OOP and it has its own records
(class members).

"Canonical" OOP should be about message passing and not all these things.

~~~
equalunique
Are you aware that records are part of OCaml, which combines with functional
programming with OOP, similar to F#?
[https://realworldocaml.org/v1/en/html/records.html](https://realworldocaml.org/v1/en/html/records.html)

------
opvasger
I don't think annonymous functions have anything special to do with FP or OO
for that matter. Maybe someone can fill in on this?

In my mind, functional programming is about programming with mathematical
functions - Purity - No arbitrary effects being thrown around - Input to
output... Ba-Da-Boom-Ba-Da-Bing.

I find this much more valuable as a restriction from the runtime than as a
guideline that can be ignored whenever deemed convenient.

That being said, I wouldn't have a job if I couldn't compromise on this :(

Is there any way to enforce some purity in F#? Does the type-system annotate
effectfulness?

~~~
Ace17
> I don't think annonymous functions have anything special to do with FP or OO
> for that matter. Maybe someone can fill in on this?

Anonymous functions are of no use until functions are first-class citizens
(otherwise, there's no way to call them!), and this has always been the case
in FP languages.

I do agree though that the focus on mathematical "purity" is a much more
defining characteristic of FP.

~~~
rbehrends
Closures were an integral part of Smalltalk's design.

And functions as first class citizens is not a particularly high bar. I mean,
C has higher order functions.

~~~
opvasger
I'm excited to use higher-order functions in C someday :)

~~~
rbehrends
You can do that now. Higher-order functions are simply functions that can take
other functions as parameters or return them as results. C has been able to do
that since forever. It's not something fancy: pretty much every programming
language has something along these lines.

What is rarer is closures, but that's a different concept. You can have
higher-order functions without having closures.

~~~
opvasger
Haha - I'm simply stating that I'm excited to do it and not for it to be
possible :P

------
DanielBMarkham
_The thesis of this article is that strategically admitting elements of OO in
an F# codebase significantly improves quality and maintainability....In my 6
years of working with F#...I typically write the implementation of a large
component in the functional style behind a private module, then expose its
public API as part of a standalone class._

I've been coding with F# since it came out. I don't claim to be a guru, but
I've coded in various production environments, both self-architected and part
of a larger group.

I write pure functional first and then refactor and abstract as necessary. I
have also decided on a policy of pure microservices: stand-alone small units
of functionality joined, tested, and deployed by the environment, not other
toolsets.

I try to keep a completely open mind about rolling my own objects. I'm an old
OO guy, I love all things OO.

Sadly, keeping to these simple principles, I find no need for objects.

At first I did. Then I got better. I keep looking for them to crop up.
Instead, what happens is that I write my microservice production code in the
domain language, with lots of descriptive parameters and methods. Then, as a I
refactor, the code splits into "neat stuff that should be part of the type
system" and "some construct that's actually solving the problem I want to
solve"

So I end up with a very small amount of domain-expressive code written using
symbols that are either in the type system or should be.

I'm done. That's it. Repeat and rinse -- keeping, of course, that small bit of
code that actually got refactored out for re-use. This code will live in a
Types.fs file. I share this file among projects.

I could be more blunt. _There are no components_. Once you start thinking in
terms of components? You've already decided you want to use OO. There's
nothing wrong with that, just don't engage in circular reasoning. If before
you begin you've already decided to use OO concepts, then you're using OO
concepts. F# has nothing to do with anything.

I'm still waiting for some good examples, and I'm trying to stay open-minded
about it. So far, though, I haven't seen the need for them. This essay didn't
change anything.

------
mookid11
Interesting how the author avoids to mention OCaml's (far from perfect, but
still) object system.

~~~
Sammi
And how F# was made by OCamlers.

