Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic way to implement "services" in F#

Tags:

f#

When working in F# and implementing a "service" pattern, such as wrappers around web APIs or databases or USB gadgets, what's the idiomatic way to do this? My inclination is to use e.g. IDatabase and Database, ITwitter and Twitter, ICamera and Camera interfaces and classes just like in C#, allowing for easy test mocks. But I don't want to code "C# in F#" if that's not the standard way to do it.

I considered using DU's, to represent e.g. Camera | TestCamera, but that means putting all the mock code into the production codebase, which sounds horrible. But maybe that's just my OO background speaking.

I also considered making IDatabase a record of functions. I'm still open to that idea, but it seems a bit contrived. Plus it rules out the idea of ever using an IoC controller, or any "MEF-like" plugin capability (at least that I'm aware of).

What's the "idiomatic F#" way of doing this? Just follow the C# service pattern?

like image 796
lobsterism Avatar asked Sep 14 '15 17:09

lobsterism


People also ask

How to implement services successfully on any Android device?

{ To implement the services successfully on any android device, it is necessary to mention the created service in the AndroidManifest.xml file. It is not possible for a service to perform its task if it is not mentioned in this file. The service name is mentioned inside the application tag.

What is an example of a service?

Playing music in the background is a very common example of services in android. From the time when a user starts the service, music play continuously in the background even if the user switches to another application. The user has to stop the service explicitly in order to pause the music.

How do users interact with the service?

Users can interact with the service by the notifications provided about the ongoing task. Such as in downloading a file, the user can keep track of the progress in downloading and can also pause and resume the process. 2. Background Services:

What is a foreground service?

Services that notify the user about its ongoing operations are termed as Foreground Services. Users can interact with the service by the notifications provided about the ongoing task. Such as in downloading a file, the user can keep track of the progress in downloading and can also pause and resume the process.


1 Answers

As mentioned in the other answer, using interfaces is fine in F# and that might be a good way to solve the problem. However, if you use more functional transformation-oriented style of programming, you may not need them.

Ideally, most of the actual interesting code should be transformations that do not perform any effects - and so they do not invoke any services. Instead, they just take inputs and produce outputs. Let me demonstrate using a database.

Using a IDatabase service, you might write something like this:

let readAveragePrice (database:IDatabase) = 
  [ for p in database.GetProducts() do
      if not p.IsDiscontinued then
        yield p.Price ]
  |> Seq.average

When written like this, you can provide a mock implementation of IDatabase to test that the averaging logic in readAveragePrice is correct. However, you can also write the code like this:

let calculateAveragePrice (products:seq<Product>) = 
  [ for p in products do
      if not p.IsDiscontinued then
        yield p.Price ]
  |> Seq.average

Now you can test calculateAveragePrice without any mocking - just give it some sample products that you want to process! This is pushing the data access out from the core logic to the outside. So you'd have:

database.GetProducts() |> calculateAveragePrice // Actual call in the system
[ Product(123.4) ] |> calculateAveragePrice     // Call on sample data in the test

Of course, this is a simplistic example, but it shows the idea:

Push the data access code outside of the core logic and keep the core logic as pure functions that implement transformations. Then your core logic will be easy to test - given sample inputs, they should return the correct results!

like image 197
Tomas Petricek Avatar answered Oct 02 '22 13:10

Tomas Petricek