7 ms·
Why OO Matters in F#
- osd 10y agoEnjoyed 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.
- denis1 10y agoIMO that is also true for Scala. And is something that gets criticized every single time I see a reference to Scala on the internet. Yet very few people acknowledge that the problem these languages are trying to solve is complex and that the solution is not always pretty because of the interaction with the platform they're supposed to run on.
- mamcx 10y agoIn the moment you get a NULL exception with a string (in a call to a .NET function) you understand that F# is part of .NET, for good and bad.
- maxxxxx 10y agoI 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 10y agoWhat 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.
- deleted 10y ago[deleted]
- pryelluw 10y agoI have the same issues. Writing idiomatic F# is a battle. Things feel always a bit off. Its a nice language that needs more love from ots creators.
- tjalfi 10y agoYou might like the talk Thirteen ways of looking at a Turtle (https://youtu.be/AG3KuqDbmhM https://youtu.be/AG3KuqDbmhM). Scott Wlaschin (https://fsharpforfunandprofit.com https://fsharpforfunandprofit.com) solves the same problem 13 different ways and demonstrates a number of techniques.
- DanielBMarkham 10y agoF# is a training language. Only know OOP? Great, we can show you how you do the same OOP stuff you like, only using F#. Want to learn some pure FP? Great, we can show you how to code idiomatically using pure FP. Want to learn some pure FP but it's tough? Great, code as much in pure FP as you like, then use a big list of "Stuff you shouldn't have to do" as a way to figure out where you need to pick up some more skills.
- rwallace 10y agoThe reason I'm using F# at the moment is indeed the platform, but not because of .Net; it's because when I tried Haskell on Windows, it didn't work, from which I conclude Haskell is primarily about Unix; I want an FP language where Windows is a first-class, no-question supported platform, and F# fits that bill.
- milesrout 10y agoWindows is first-class-no-questions-asked-supported by Haskell. The same cannot be said about .NET's support for platforms other than Windows.
- rwallace 10y ago> Windows is first-class-no-questions-asked-supported by Haskell. I would have hoped so, but when I tried it recently, the package managers didn't work. http://stackoverflow.com/questions/42562702/haskell-on-windows-stack-fails-to-fetch-package-index http://stackoverflow.com/questions/42562702/haskell-on-windo... http://stackoverflow.com/questions/42564296/haskell-on-windows-cabal-fails http://stackoverflow.com/questions/42564296/haskell-on-windo... > The same cannot be said about .NET's support for platforms other than Windows. Now that is an interesting question. A few years ago when I tried compiling and running a C# program on Linux/Mono it worked fine - and I'm no Linux expert to be able to debug hairy problems, so it had to work pretty much out of the box - and I'm told things have improved since then, but for all I know, there could be problems I'm not aware of. Have you run into any?
- milesrout 10y agoTaking each of those in turn: The first question is an issue because of Stack assuming something about how git works that might not be true on your system. That's probably because you have an old version of git installed. I had a similarish issue on Gentoo, where it assumes something about gcc that isn't true in the version of gcc I have installed. That's a bug in stack, it's not really Windows-specific. Try uninstalling git, installing the latest version of git, or something like that. Your second question has nothing to do with Windows. The issue is that when one installs packages globally (as you are doing with cabal in your question), you get into issues if you have two incompatible packages. For example, you want stylish-haskell<0.6 for hfmt, and stylish-haskell requires directory<1.3, but you have 1.3 installed (because of another package). That's called 'cabal hell'. It's basically just dependency hell for Haskell. It was common in Python before virtualenv became popular, and common in Haskell before stack became popular. In fact, a big reason for using stack is to make it easier for people new to Haskell, and people on Windows, and in particular people new to Haskell on Windows, to use Haskell. The way it gets rid of this issue is that you install dependencies locally, separately, for each project. At the end of the day, this really sucks for you. I hope you can get it working, Haskell is a lot of fun. You should post the first issue to https://github.com/commercialhaskell/stack/issues/new https://github.com/commercialhaskell/stack/issues/new.
- bad_user 10y agoI 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 10y agoAn 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.
- bad_user 10y ago"Function style" can also be OOP, FP being imo orthogonal, although it is true that in practice, with FP, polymorphism is usually achieved with parametric polymorphism because they play well together. Speaking of parametric polymorphism, the need to modify a type's behaviour by implementing a new type is alleviated if you go with higher-kinded polymorphism, where the implementation bits are abstracted by a generic F[_] type and using type classes to go along with that. In the example I'm giving in that presentation, I'm implementing a type named Iterant, for which the plugged-in F[_] can dramatically modify its behaviour. So in practice you don't need to implement a new kind of Iterant, which wouldn't make much sense anyway, you only need to implement a new F[_], which is used to abstract away the details of how Iterant gets evaluated, which is the part that people want to change. Unfortunately most statically typed mainstream languages are not capable of expressing such abstractions due to lack of higher kinded types, or ways to encode / express type-classes, although this part is less problematic than lack of HKTs.
- opvasger 10y agoI 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?
- ungzd 10y agoWhen I last used F#, I had no problems with purity — it's not enforced but it's a default in standard library and it's convenient.
- Ace17 10y ago> 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 10y agoClosures 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 10y agoI'm excited to use higher-order functions in C someday :)
- rbehrends 10y agoYou 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.
- romaniv 10y agoI'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_oop_en http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay... Two papers he references: https://www.rand.org/content/dam/rand/pubs/research_memoranda/2007/RM5290.pdf https://www.rand.org/content/dam/rand/pubs/research_memorand... http://repository.cmu.edu/cgi/viewcontent.cgi?article=2281&context=compsci http://repository.cmu.edu/cgi/viewcontent.cgi?article=2281&c... Cool article on failures of OOP: http://www.smashcompany.com/technology/object-oriented-programming-is-an-expensive-disaster-which-must-end http://www.smashcompany.com/technology/object-oriented-progr... 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.
- ungzd 10y agoOO 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 10y agoAre 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
- saosebastiao 10y agoMirror 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 10y agoYou 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- 10y agoML uses "functors" in a different sense than e.g. Haskell.
- saosebastiao 10y agoIn ML a functor is a very different thing from Haskell. It is akin to a higher order module. http://stackoverflow.com/a/16353711 http://stackoverflow.com/a/16353711
- tel 10y agoClasses and functors are still a bit orthogonal. There are tons of things you can do with classes nowadays depending on what language you're working in, but essentially, fundamentally, functors are about types and classes are about value construction. Many type-level things have been ported over to classes (information hiding, parameterization, sometimes even type members) but these are more clearly functor-like ideas. So it doesn't surprise me to think that classes can emulate functors when their functionality is often built by emulating functors while functors can't emulate classes since functors as they're implemented in SML/OCaml assume that value construction will just be done by plain functions (or OCaml's class construct). This gets back the one of the cruxes of the LSP—it's about subtyping whereas many class systems are about subclassing and those two things actually just have different behavior. So perhaps the one thing that functors do---and the thing that if I were a F# programmer I'd guess is the thing that I'd miss---is intentionally not do things that classes do. Functors are a narrower interface which makes it simpler to reason about ("Liberties constraint, constraints liberate").
- Ace17 10y agoWhatever 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 10y agoThis 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
- mookid11 10y agoInteresting how the author avoids to mention OCaml's (far from perfect, but still) object system.
- Sammi 10y agoAnd how F# was made by OCamlers.
- DanielBMarkham 10y agoThe 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.