Transition from static configuration to dynamic configuration

I am working on a haskell project, where the settings are currently in a file called Setting.hs , so they are checked at compile time and can be accessed globally.

However, since this is too static, I planned to read the configuration at runtime. The code base is huge, and it would seem like a lot of effort to go through the setup, for example. as an argument through the entire program stream, since they can be arbitrarily accessed from anywhere.

Are there any design patterns, libraries, or even ghc extensions that can help here without reorganizing all the code?

+6
source share
2 answers

Thanks for the tips! I came up with a minimal example that shows how I will deal with the reflection package:

 {-# LANGUAGE Rank2Types, FlexibleContexts, UndecidableInstances #-} import Data.Reflection data GlobalConfig = MkGlobalConfig { getVal1 :: Int , getVal2 :: Double , getVal3 :: String } main :: IO () main = do let config = MkGlobalConfig 1 2.0 "test" -- initialize the program flow via 'give' print $ give config (doSomething 2) -- this works too, the type is properly inferred print $ give config (3 + 3) -- and this as well print $ give config (addInt 7 3) -- We need the Given constraint, because we call 'somethingElse', which finally -- calls 'given' to retrieve the configuration. So it has to be propagated up -- the program flow. doSomething :: (Given GlobalConfig) => Int -> Int doSomething = somethingElse "abc" -- since we call 'given' inside the function to retrieve the configuration, -- we need the Given constraint somethingElse :: (Given GlobalConfig) => String -> Int -> Int somethingElse str x | str == "something" = x + getVal1 given | getVal3 given == "test" = 0 + getVal1 given | otherwise = round (fromIntegral x * getVal2 given) -- no need for Given constraint here, since this does not use 'given' -- or any other functions that would addInt :: Int -> Int -> Int addInt = (+) 

The Given class is a bit easier to work with and ideal for a global configuration model. All functions that do not use Given (which gets the value) do not seem to require a class constraint. This means that I only need to change the functions that actually access the global configuration.

This is what I was looking for.

+4
source

What do you ask, if possible, will break link transparency, at least for a pure function (the result of a pure function may depend on some global variables, but not on the configuration file, could it)?

Usually people avoid this type of situation by passing implicitly configuration in the form of data through Monad. Alternatively (if you are happy to reorganize your code a bit), you can use the implicit extenson parameter , which was theoretically created to solve this type of problem, but in practice it really does not work. However, if you really need to, you can use unsafePerformIO and ioRef to have an upper level mutable state that is dirty and frowned up. You need to change the state with a high level, because you must be able to change the "mutate" of your initial configuration when loading it.

Then you get things like this:

 myGlobalVar :: IORef Int {-# NOINLINE myGlobalVar #-} myGlobalVar = unsafePerformIO (newIORef 17) 
0
source

Source: https://habr.com/ru/post/986865/


All Articles