Potentially interesting question: How to quantify 'functional-ness'

So, I just finished writing a small WebSocket server and client, twice.

Well, three times really, but that’s a different story.

I have both codebases side by side, and I’m trying to figure out which one I want to keep more, and one metric I was considering was the relative ‘functional’ attributes between the two.

Without going into a lot of details, one uses a common thread-safe mutable dictionary, some custom Event objects, and the IEvent/Observable pattern to handle those Events.

The other uses a normal list, MailboxProcessors and contains more Types to keep things organized. It unfortunately has an uncomfortable bodge that I don’t like.

I want to try and evaluate them on functional merits but I’m too new to the language to have a lot of ‘taste’ myself. I’ve been considering things like number of functions returning unit, overall composability (number and length of pipelines), what is mutable, etc.

Does anyone have any metrics they like to use when looking at two similar projects, assuming things like performance are the same or very similar?

Not trying to be “that guy”, but…

  1. There’s a whole bunch of (mostly non-technical) context that’s needed before any sort of opinions can be formed about one, or another, particular solution to a given problem.
  2. What is “functional-ness”? What is “FP”? Too much worry about labels is ultimately just a waste of time – especially if you’re tying to learn F#.

Instead, here are two good techniques for learning:

  1. Maybe there’s a specific feature or technique about which you have questions, especially as compared to a different feature or technique (e.g. function pipeline versus a computation expression)?
  2. Maybe you want to post a sample, with a thorough explanation of the constraints/requirements/context, and ask how others might approach the problem?

Those are reasonable points.

  1. I’m not asking for opinions on my exact implementation. Let’s assume for the moment that I’m not at liberty to share more than I have. I’m looking for what others have used as a benchmark or guideline when evaluating other code.
  2. I disagree. There’s idiomatic code in any language, and there’s “trying to bend the code to do things in a way that isn’t natural.” If you like, what’s a good way to tell when that is happening?

Have you read the F# Coding Conventions doc? https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/conventions

Overall principles here are also worth keeping in mind: https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/#five-principles-of-good-f-code

In general:

  • If performance matters, especially high-allocation, a functional interface (pure inputs and outputs) over a mutable implementation is great. That should be encouraged.
  • If performance is less of a concern, then the less mutation the better.

Off-hand, it sounds like the second one is a bit more idiomatic F#. Idiomatic F# can be difficult to define since it always depends on your scenario, but the IEvent/Observable pattern isn’t really rooted in typed functional programming. F# types that are processed in a MailboxProcessor is squarely an “F# thing”, though.

1 Like

Firstly, thank you. You addressed my question without telling me my question was wrong.

I have previously read those two documents you linked, and I’ve absorbed what I can from them, at least at my current level of understanding and experience. But I will make a point of going back over them periodically just in case something ‘clicks’ that didn’t before, or perhaps I just get a different take.

I guess what I’m most looking for is for someone to describe their ‘taste’ in simple terms, even if those terms lack nuance. It’s just guidelines. I have the ‘official’ opinion from those documents, but there’s tons of things they don’t address. Which is fine.

So bits like “this is probably more idiomatic than that” are very useful to me, even if it is just your opinion. I can aggregate opinion and experience into something that, hopefully, I’m able to give back as my sense of ‘taste’ to the next person who is trying to orient themselves in this language.

I think Jimmy sums it up very eloquently here:

Over the years, I’ve given conference talks on functional programming, written blog posts, held workshops etc. One of the questions I often get is: how do I know that my F# code is functional?

This is a normal question to ask. When I started learning F# in 2010, I asked myself the same question for years.

I think it’s a reasonable question to ask for a couple of reasons. My ow motivation was originally just curiosity. I was curious to learn about other ways of doing things, because of the trivial observation that I can’t pick the best way to perform a task if I only know of one way to do it.

Another reason to ask that question is that functional programming offers a way of organising code that composes better than code based on mutations, circular references, etc. There are fundamental reasons why this is so, but that’s too big a topic to cover here. The bottom line is that FP may offer a way to structure code bases so that they’re easier to maintain over time.

On the other hand, as @cartermp implies, there may be performance gains to be had from mutation. As always, it’s a question of trade-offs. Usually, I prefer the F# package over, say, C#…

So, how do I know that my F# code is functional?

The language itself gently nudges you in the direction of FP, but not by much. It’s a friendly language, with the big advantage that it enables you to gradually learn FP. Considering that most people come to FP from OOP, I don’t think this advantage can be understated, but it also means that you may be stuck in a depression of object-oriented F#.

For a beginner, I don’t think that the above question is important, but eventually, if you like the language, you may start to wonder about that.

I wrote a blog post trying to answer that question a few years back. The bottom line is that the foundation of functional programming is referential transparency. Most other benefits follow from that foundation.

Pure functions give you referential transparency, so the larger a part of your code base consists of pure functions, the closer you get.

2 Likes

Mark, it’s really cool to interact with you on this topic. I had read several of your posts back when I was first coming around to deciding to pick up the language or not. I remember that some of it made sense, and some of it seemed like voodoo mysticism (in a good way). I need to circle back around to your material because I bet a lot more of it would make sense to me now.

Touching on the notion of the language gently nudging but not, I suppose, ‘demanding’ that code be written in a particular way is something that’s been an issue for me. I actually enjoy it when I can find someone who explains why I should use one feature over another. Or why I should employ some language construct to produce a given benefit. I agree that the language is ‘pragmatic,’ but I want to avoid taking advantage of that pragmatism because it feels like an admission. ‘Good enough…’ it sighs metaphorically at me.

As an example, I found the site dev.to the other day, and I read this article about creating a wrapper around a BCL class. And it starts off with a “this will work, but let’s see if we can’t make it better” approach that I just really liked. That’s what I’m continually trying to do. “Is this just ‘good enough’? Can I go deeper?”

I get the feeling I’m going to be wrestling with this for a long time. :slight_smile: I appreciate everyone’s contributions to the discussion thus far.

1 Like