1

I'm pretty new to functional programming so this might be a question due to misconception, but I can't get my head around this - from an OOP point of view it seems so obvious...


scenario: Assume you have an actor or micro-service like architecture approach where messages/requests are sent to some components that handle them and reply. Assume now, one of the components stores some of the data from the requests for future requests (e.g. it calculates a value and stores it in a cache so that the next time the same request occurs, no calculation is needed). The data can be hold in memory.

question: How do you in functional programming in general, and especially in f#, handle such a scenario? I guess a static dictionary is not a functional approach and I don't want to include any external things like data stores if possible.

Or more precise: If an application creates data that will be used later in the processing again, where do we store the data?

example: You have an application that executes some sort of tasks on some initial data. First, you store the inital data (e.g. add it to a dictionary), then you execute the first task that does some processing based on a subset of the data, then you execute the second task that adds additional data and so on until all tasks are done...

Now the basic approach (from my understanding) would be to define the data and use the tasks as some sort of processing-chain that forward the processed data, like initial-data -> task-1 -> task-2 -> ... -> done but that does not fit an architecture where getting/adding data is done message-based and asynchronous.

approach:

My initial approach was this

type Record = { }

let private dummyStore = new System.Collections.Concurrent.ConcurrentBag<Record>()

let search comparison =
    let matchingRecords = dummyStore |> Seq.where (comparison)
    if matchingRecords |> Seq.isEmpty
    then EmptyFailedRequest
    else Record (matchingRecords |> Seq.head)

let initialize initialData = 
    initialData |> Seq.iter (dummyStore.Add)

let add newRecord =
    dummyStore.Add(newRecord) 

encapsulated in a module that looks to me like an OOP approach.

After @Gustavo asked me to provide an example and considering his suggestion I've realized that I could do it like this (go one level higher to the place where the functions are actually called):

let handleMessage message store = 
    // all the operations from above but now with Seq<Record> -> ... -> Seq<Record>
    store

let agent = MailboxProcessor.Start(fun inbox-> 

    let rec messageLoop store = async{
        let! msg = inbox.Receive()

        let modifiedStore = handleMessage msg store

        return! messageLoop modifiedStore 
        }
    messageLoop Seq.empty
    )

This answers the question for me well since it removed mutability and shared state at all. But when just looking at the first approach, I cannot think of any solution w/o the collection outside the functions


Please note that this question is in f# to explain the environment, the syntax etc. I don't want a solution that works because f# is multi-paradigm, I would like to get a functional approach for that.

I've read all questions that I could find on SO so far but they either prove the theoretical possibility or they use collections for this scenario - if duplicated please point me the right direction.

MrWombat
  • 649
  • 9
  • 19
  • 1
    Having static and immuttable map/dictionary for already calculated value is ok for functional programming too. You just combine/chain to functions 1. GetFromCache 2.If Cache not exists calculate. Option type is perfect candidate for returning from GetFromCache functions – Fabio Oct 03 '17 at 05:25
  • You might also want to read up on what closures are. – Jared Smith Oct 03 '17 at 11:32

1 Answers1

4

You can use a technique called memoization which is very common in FP. And it consists precisely on keeping a dictionary with the calculated values.

Here's a sample implementation:

open System
open System.Collections.Concurrent

let getOrAdd (a:ConcurrentDictionary<'A,'B>) (b:_->_) k = a.GetOrAdd(k, b)

let memoize f =
    let dic = new ConcurrentDictionary<_,_>()
    getOrAdd dic f

Note that with memoize you can decorate any function and get a memoized version of it. Here's a sample:

let f x =
    printfn "calculating f (%i)" x
    2 * x

let g = memoize f // g is the memoized version of f

// test

> g 5 ;;
calculating f (5)
val it : int = 10

> g 5 ;;
val it : int = 10

You can see that in the second execution the value was not calculated.

Gus
  • 25,839
  • 2
  • 51
  • 76
  • Thank you for your answer! I think I get the point, but it does not really fit into the "distributed architecture" I was describing, right? I've added an example to my initial question to be more precise about my scenario – MrWombat Oct 03 '17 at 07:57
  • @MrWombat I think it fits. Why not? Can you add a small sample code? – Gus Oct 03 '17 at 08:24
  • @MrWombat Reading at your description, it seems to me that the initial-data function is the one to be memoized. – Gus Oct 03 '17 at 10:21
  • 1
    Thank you so much, I think I've got your idea now and it helped me to find a solution (maybe you can check if that fits your suggestion). – MrWombat Oct 03 '17 at 10:57