Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Functional programming and decoupling

I'm your classic OOP developer. However since I discovered purely functional programming languages I've been ever intrigued to the why since OOP seemed to solve most business cases in a reasonable manner.
I've now come to the point in my software development experience where I'm seeking more concise and expressive languages. I usually write my software in C# but for my latest project I decided to take the leap and build a business service using F#. In doing so I'm finding it very hard to understand how decoupling is done with a purely functional approach.

The case is this. I have a data-source, which is WooCommerce, but I don't want to tie my function definitions to that specific data source.
In C# it is apparent to me that I want a service that looks something like this

public record Category(string Name);

public interface ICategoryService
{
    Task<IEnumerable<Category>> GetAllAsync();
}

// With a definition for the service that specifies WooCommerce
public class WcCategoryService : ICategoryService
{
    private readonly WCRestEndpoint wcRest;

    // WooCommerce specific dependencies
    public WcCategoryService(WCRestEndpoint wcRest)
    {
        this.wcRest = wcRest;
    }

    public Task<IEnumerable<Category>> GetAllAsync()
    {
        // Call woocommerce REST and map the category to our domain category
    }
}

Now in the future, if and when I decide we need a new store for providing categories I can define a new implementation for that specific service, replace the injected type and not mess up the dependents because of this change.

Trying to understand how the functional dependency approach is solved I was met with this case (reading "Domain Modeling made functional") where the type signature directly define the dependencies, so the above C# equivalent would turn into a highly coupled definition

type Category = { Name: string }
type GetCategories =
    WCRestEndpoint
    -> Category list

Suddenly if I am to change the source of categories I would have to either change the functional signature or provide a new definition to be used which would ripple through the application and thereby not be very robust.

What I'm curious about is whether I'm misunderstanding something fundamental.

With my OOP brain all I can think of doing is something like this

type Category = { Name: string }

// No longer directly dependent on WCRestEndpoint
type GetCategories = unit -> Category list

// But the definition would require scoped inclusion of the dependency
// Also how does the configuration get passed in without having the core library be dependent on the Environment or a config in the assembly?
let rest = WCRestEndpoint(/* Config... */)

type getCategories: GetCategories = 
    fun () ->
        let wcCategories = rest.GetCategories()
        // Convert the result into a Category type

I've looked around and I haven't found any explanation as to how change is handled with a purely functional approach, which is what led me to believe there is something fundamental I've misunderstood.

How do you expose a functional API without tying the function type signatures up in implementation specific types? Am I thinking about this wrong?

like image 509
Bjarke Sporring Avatar asked Jun 28 '21 09:06

Bjarke Sporring


People also ask

Is functional programming good for parallel computing?

Functional Programming – AdvantagesEfficient Parallel Programming − Functional programming languages have NO Mutable state, so there are no state-change issues. One can program "Functions" to work parallel as "instructions". Such codes support easy reusability and testability.

What is functional way programming?

Functional programming is a programming paradigm in which we try to bind everything in pure mathematical functions style. It is a declarative type of programming style. Its main focus is on “what to solve” in contrast to an imperative style where the main focus is “how to solve”.

What is decoupling in Javascript?

Decoupling with Events Pub sub stands for publish subscribe', and it is one of the tried and true methods of decoupling two software systems from on another. In the case of Javascript Events, the two systems that are being decoupled are the DOM and the Javascript program that you have written as the developer.

What are the three functions for functional programming?

These three functions, which provide a functional programming style within the object-oriented python language, are the map(), filter(), and reduce() functions.


Video Answer


3 Answers

I struggled with this question for years before I realised that I was looking at it the wrong way. Coming from object-oriented development and Dependency Injection, I kept looking for a functional alternative to Dependency Injection. I finally realised that Dependency Injection makes everything impure, which means that you can't use that approach (not even partial application) if you want to apply a functional architecture.

The red herring is to focus on the dependencies. Instead, focus on writing pure function. You can still use the Dependency Inversion Principle, but instead of focusing on actions and interactions, focus on data. If a function requires some data, pass it as an argument. If a function has to make a decision, return it as a data structure.

You don't provide any examples of where you'd want to use a list of Category values, but a function that depends on such data would have a type like this:

Category list -> 'a

Such a function is completely decoupled from the source of the categories. It only depends on the Category type itself, which is part of the Domain Model.

Ultimately, you'll need to get the categories from somewhere, but this work you push to the boundary of the system, e.g. Main:

let Main () =
    let categories = getCategories ()
    let result = myFunction categories
    result

Thus, if you change your mind about how to get the categories, you only have to change one line of code. This kind of architecture is akin to a sandwich, with impure actions surrounding the pure heart of the application. It's also known as functional core, imperative shell.

like image 61
Mark Seemann Avatar answered Oct 13 '22 11:10

Mark Seemann


I don't think there is a single right answer to this, but here is a couple of points to consider.

  • First, I think real-world functional code often has a "sandwich structure" with some input handling, followed by purely functional transformation and some output handling. The I/O parts in F# often involve interfacing with imperative and OO .NET libraries. So, the key lesson is to keep the I/O to the outside and keep the core functional handling separate from that. In other words, it makes perfect sense to use some imperative OO code on the outside for input handling.

  • Second, I think the idea of decoupling is something that is more valuable in OO code where you expect to have complex interfaces with intertwined logic. In functional code, this is (I think) much less of a concern. In other words, I think it is perfectly reasonable not to worry about this for I/O, because it is only the outer side of the "sandwich structure". If you need to change it, you can just change it without touching the core functional transformation logic (which you can test independently of I/O).

  • Third, on the practical side, it is perfectly reasonable to use interfaces in F#. If you really wanted to do the decoupling, you could just define an interface:

    type Category { Name: string }
    
    type CategoryService = 
       abstract GetAllAsync : unit -> Async<seq<Category>>
    

    And then you can implement the interface using object expressions:

    let myCategoryService = 
      { new CategoryService with 
        member x.GetAllAsync() = async { ... } }
    

    Then, I would have a main function that transforms the seq<Category> into whatever result you want and this would not need to take CategoryService as argument. However, in your main bit of code, you would take this as argument (or initialize it somewhere when the program starts), use the service to fetch the data and call your main transformation logic.

like image 40
Tomas Petricek Avatar answered Oct 13 '22 12:10

Tomas Petricek


If all you want is to not use objects, it's a fairly mechanical rewrite.

A single-method interface is just a named function signature, so this:

public interface ICategoryService
{
    Task<IEnumerable<Category>> GetAllAsync();
}

async Task UseCategoriesToDoSomething(ICategoryService service) {
    var categories = await service.GetAllAsync();
    ...
}

becomes:

let useCategoriesToDoSomething(getAllAsync: unit -> Async<seq<Category>>) = async {
    let! categories = getAllAsync()
    ...
}

Your composition root becomes a matter of partially applying functions with the concrete implementations of these function arguments.

That said, there is nothing wrong with using objects as such; F# mostly rejects mutability and inheritance, but embraces interfaces, dot notation, etc.

There's a great slide on OO in F# from a talk Don Syme gave: enter image description here

like image 5
Asik Avatar answered Oct 13 '22 12:10

Asik