Preferred way of load application settings in f#

Hi

I have library using netstandard 2 and consuming it from a dotnet core 3.1 app and want to get some settings from settings.json or app.config. How do i do that in F#? What is the preferred way here?

I heard about

  1. using using the configuration builder (like c#), it needs the package FsConfig

type Config = {
processId:int
}
let configurationRoot =
ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(“settings.json”).Build()
let result = appConfig.Get ()

this is somehow giving me this error

System.IO.FileLoadException: ‘Could not load file or assembly ‘TypeShape, Version=8.0.1.0, Culture=neutral, PublicKeyToken=null’. The located assembly’s manifest definition does not match the assembly reference. (0x80131040)’

  1. using a typeprovider Fsharp.Configuration https://fsprojects.github.io/FSharp.Configuration/ this looks promising but in my dotnet core app it wouldnt work.

open FSharp.Configuration
type Settings = AppSettings<“app.config”>
Settings.FlagsFileSource

Thats what i am getting when i want to access the settings, last line above

System.IO.FileNotFoundException: ‘Could not load file or assembly ‘System.Configuration.ConfigurationManager, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51’. Das System kann die angegebene Datei nicht finden.’

I added this package “System.Configuration.ConfigurationManager,”

then i got this?

System.TypeLoadException: ‘Could not load type ‘System.Web.Hosting.HostingEnvironment’ from assembly ‘System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’.’

Any hints?

Looks like with the new alpha2 version of FSharp.Configuration its working.

Anyway, is this the way to go in F#?

Do you want to consume the settings in the library or in the application?

If the latter, I would just use the classes in Microsoft.Extensions.Configuration (i.e. the standard configuration and options classes documented here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1 … don’t let the “ASP.NET” fool you – works perfectly well for non-web applications, too).

if the former, it’s somewhat harder to make a recommendation without some more context (e.g. into what envirnments will it be deployed, and so on).

2 Likes

Hi @pblasucci

I guess I have some settings in my library that i definetely need but wouldnt want to hard code.
My current solution is to use FSharp.Configuration in the latest alpha version. That works now. I have an app.config in my library project to satisfy the type provider and an app.config in my f# application to get the actual values from.

So for now that works. What i dont know is how i refresh the type provider once that has loaded some values. I posted this question here too.

Though I’ll give that Microsoft.Extensions.Configuration option another shot. Since I know that from my c# web project past.

Would you say that the type provider is the f#est way? Dont mind my asking I am a newbie in f#. :slight_smile:

Opinions will, of course, vary, but…

I’d say the “F#est way” is whatever gets the job done in a way that you (or your team) feel comfortable maintaining. Could be a type provider. Could be a 3rd-party library. All depends. But absent any other criteria to help “narrow the field”, just using whatever is in the .NET Standard Library is a safe default.

Hope this helps. :blush:

1 Like

In case this might help you, I’m currently loading JSON config with FSharp.Data’s JSON provider. Here is the code.

2 Likes

The following has nothing to do with F#, but is just a general observation regarding good API design.

If you read configuration values from a configuration system from within a library, you’ve now coupled the library to the configuration system. That coupling is implicit, because you can compile calling code which will fail at run time (if the configuration file isn’t present).

It makes it harder to use the library. It makes it harder to unit test the library.

A better option is to design a library’s API so that it takes a set of options as input. Then the caller can supply the configuration options read from whichever source it wants.

With this principle, you can’t compile the calling code if you don’t supply the configuration values.

1 Like

Hello Mark.

Thanks for the hint. Thats actually what I’d like to do in the end. The thing is that I am still learning the concepts and coming from C# the config approach is more natural in the first place.
Can you point to me to a good example of your approach? Greatly appriciate that.

What I wrote also applies to C#, and most other languages that I’m aware of.

If, for example, you need an integer configuration value, design your functions so that they take an integer value as a parameter. In F#, it often pays to make the configuration values the leftmost parameters, because that means that callers can partially apply them.

If you need to pass around many configuration values, you can declare a type that encapsulates them.

If you can provide some example code that illustrates what you’re trying to do, I’ll be happy to give an example.

I’d like to know how what I did can be improved in regard of your comment @ploeh.
In my library I have an AppConfig module using the JSON type provider and defining functions to access the config values because the type is hidden in AppConfig.fsi. The application can then load the file and subsequently pass the object loaded by the AppConfig module to functions and types.
My library code is clearly coupled in the way you described. I experience the downside of the coupling because for running my tests I am now required to have a json configuration file. How would you fix this?

I don’t know that particular code base, but from glancing over it, it looks to me as though the part that’s causing you grief is a symptom of what’s almost always a fundamental problem in FP: impurity.

While you can load the configuration from disk with the load action, that’s the only option for creating a value of the type t (why is it called t, by the way?). That action is impure.

I would suggest that providing a pure function that can create a value of the the type t would make things much simpler.

1 Like

thanks for your advice, I’ll take a shot at it.
This is a project I’ve been learning F# with, and (I hope) my FP approach improves over time. I think the t naming comes from OCaml code I’ve seen where the hidden type was called t.