I hear you. While the impurity and the object-oriented nature is inherent with being a .NET language (not for long we hope), you should view this as "an added benefit" (not literally, naturally) that gives a more diverse tool set for those who know how to use it.

Your comment on adequately embracing the functional style is very important. Some see this as a two step process: first convert non-functional programmers, then teach them proper functional programming. It's a compromise that seems to work better as things stand, even if we don't like it.

By on 2/20/2012 3:19 PM ()

Knowing when and how to apply both OO and functional techniques in F# has been my largest source of befuddlement. It's one reason I have spent many a leisure hour reading the core library source and other professionally written F# - trying to find that balance and to gain a natural sense of the proper application of each technique in a more well-rounded style. In your "Expert F# 2.0" you (et al) say "It’s a common misconception that the functional and OO programming methodologies compete; in fact, they’re largely orthogonal." I'd like to see more information and education from experts on the topic of combining the architecture/library/packaging techniques of OO with the algorithmic and targeted-solution elegance of functional programming. Without such guidance it's easy to get the idea that module+datatype=good and class+methods=bad.

You said:
"While the impurity and the object-oriented nature is inherent with being a .NET language (not for long we hope)..."

Can you explain that parenthetical statement?

By on 2/26/2012 7:18 AM ()

I would encourage you to read Tell Above, and Ask Below - Hybridizing OO and Functional Design by Michael Feathers and a follow up by Jessica Kerr.

The thing is, true OO is orthogonal. C# is primarily a Class-Oriented language, so you generally still follow a more imperative flow. As Alan Kay noted, "OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP." With agents (MailboxProcessor<'T>), F# can actually achieve true OO.

By on 3/27/2012 6:54 AM ()

Also, does anyone see the <?> symbols all over my post? They are inserted in place of my newlines. I wonder how I get rid of them? :)

By on 3/29/2012 1:55 PM ()

I don't see them (what browser do you use?), but I see a problem when I try to edit your comment. Filed a ticket - will investigate.

By on 4/6/2012 6:15 AM ()

i also see them. No clue how to get rid of them, though.

By on 3/30/2012 6:12 AM ()

I wonder if Micheal has taken into account the structuring of large programs entirely with monads or continuations with CPS?

Monads seem to me to be the best way to structure programs. They are more modular than objects, and are more formal. OO program structures are ad hoc, with no two teams ever seeming to come up with usefully-similar program layouts. Monads are so formal that we can codify the abstraction and build common operations over them. The structure of each OO system must be learned by an oncoming team member nearly from scratch - this is very difficult and costly. With monads, an oncomer need learn about monadic style just once, and can leverage the monad pattern to quickly ascertain how an unfamiliar and complex program is structured.

I understand that traditional FP programs built with F# / ML / etc. are similarly structured to OO programs, but I think that might be because of inherent language impurities. With F# et al, we are better able to reason about algorithmic behavior, but still seem to lose the ability to reason about 'higher level' behavior - sharing the many of the same problems we had back when we used just OO (and this is coming from a someone who strongly prefers F# over Haskell).

In the case of dispersed communicating processes, I think he's right on by advocating Erlang's message passing model. However, it's unclear to me how that is relevant to structuring a program's high-level internals.

To me, Michael does not seem to account for the alternative program structuring mechanisms such as continuations and monads. Mostly-pure functional programming with monads seems to me to yield code that is most able to be reasoned about at both the low and high levels. And continuations, while devious in many ways, still seem worth accounting for.

After reading the article, I still conclude that OO and FP have about the same relationship as a screw driver and a hammer - they are each valuable where applicable, but attempting to merge them into a single tool still seems wrong.

By on 3/29/2012 1:51 PM ()

And now I've realized that I've not answered the question directly. If you take for granted that pure FP is the best default in F#, then I believe it follows that it is best to stick with pure FP facilities by default (modules+records+unions+functions). I would only use object-oriented facilities (interfaces+classes+methods) when leveraging the OO paradigm for a specific reason, or when I have an otherwise specific need. This makes sense to me in practice as sticking to functional facilities allows you to avoid lots of type annotations, makes code more portable between OCaml et al, have functions available to pass around (can we easily pass instance methods in F#?), can curry function calls (methods typically use tuples and thus won't be curriable). All this should afford us more idiomatic F# (that is, as soon as we can establish what we collectively mean by idiomatic F#!)

By on 2/27/2012 2:07 PM ()

I know Michael's question is not aimed at me, but I do feel I have useful input.

My mental model is that the primary thing we're after when programming is producing code that we can most easily reason about. Pure functional programming gives us precisely that and is adequately supported in F#, so that's why I consider it the best default. Sometimes it is inadequate however, and we have to selectively use other techniques.

One concrete example of when to abandon pure FP is when you need an abstractly stateful machine to represent a plug-in or an IO device. Anything whose representation must be opaque is therefore best represented with an interface or abstract class. Abstractly stateful machines use late-binding to become one of the most powerful and flexible tools in software development, but are accordingly the hardest to reason about. This is combinatorally problematic when you have such machines that are related recursively and bidirectionally (typical of OO software). This is why OOP is a bad default in my opinion, but also very appropriate when you need exactly it.

Another concrete example is when you need fast computation over a large data set. Imperative procedures become indispensable when you need operations that scale over very large data. This set of idioms has been known as procedural programming, array-based programming, or as data-oriented programming. (I particularly like the data-orientation paradigm because it places specific emphasis on being cache-aware, among other things). This is great stuff for writing a physics engine or transformation pipeline. It uses the computer's panache for in-place mutation to achieve maximum speed by leveraging locality of reference and direct data representation. However, the use of mutation and lack of data abstraction makes the code harder to reason about. This, like OOP, makes it a bad default IMO, but invaluable where appropriate.

Another example is where you need to implement a ton of very similar features without having an explosion of code to write. I typically approach this with declarative programming. Declarative programming gives us a higher, more customized semantic context in which to write code. Once we have lifted our programming context out of general-purpose computation, we can very quickly implement the features that are closed (algebraically) under the lifted context. An example is a game's special effects written in XML and interpreted at run-time to produce cool magic spells. Double bonus when you can actually write a code generator that produces machine code (VM or otherwise) that leverages data-orientation techniques for maximum performance. The downside is that you either end up with an interpreter that is slow, or a code generator that is a lot of work to implement - not to mention more code to step through when debugging. Haskell monads are a nice balance of perf and implementation difficulty, but due to laziness are very hard to reason about in terms of performance, and do have limited expressiveness compared to external declarative implementations.

The last example I can think of right now is when you are in an optimization phase and you need to get more responsiveness out of your program. Transforming your code in hotspots to use in-place mutation can improve scaling significantly. This probably doesn't require further explanation as it is likely obvious as to why optimization is desirable only when required.


By on 2/26/2012 12:55 PM ()

Ah, so it's a two step process. That makes a lot more sense to me now. I was getting the feeling that people were just learning F# and stopping there. I guess I approached it the opposite way in that I learned FP style and only then started using an functional language. I didn't see that others were doing the same but with a different approach :)

By on 2/21/2012 12:32 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us
Built with WebSharper