我正在开发一个haskell项目,其中的设置当前位于名为Setting.hs
的文件中,因此在编译期间会对它们进行检查,并且可以全局访问。
但是,由于这有点太静态,我正在考虑在运行时读取配置。代码库是巨大的,似乎通过设置是相当大的努力,例如作为整个程序流程的参数,因为它们可以从任何地方任意访问。
是否有任何设计模式,库甚至ghc扩展可以在不重构整个代码的情况下提供帮助?
答案 0 :(得分:4)
感谢您的提示!我想出了一个最小的例子,它展示了我将如何使用reflection包:
{-# 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 = (+)
Given
类更易于使用,非常适合全局配置模型。所有不使用given
(获取值)的函数似乎都不需要类约束。这意味着我只需要更改实际访问全局配置的函数。
这就是我要找的东西。
答案 1 :(得分:0)
你在问什么,如果有可能会破坏参照透明度,至少对于纯函数来说(纯函数结果可能依赖于某些全局变量,但不能在配置文件中取决于它)?
通常人们通过Monad隐式地将配置作为数据传递来避免这种情况。或者(如果您愿意稍微重构一下代码),您可以使用implicit parameter extenson,理论上已经解决了这类问题,但实际上并没有真正起作用。
但是,如果您确实需要,可以使用unsafePerformIO
和ioRef
来使top level mutable state变得脏兮兮的皱眉。您需要一个顶级可变状态,因为您需要能够在加载初始配置时修改“mutate”。
然后你会得到类似的东西:
myGlobalVar :: IORef Int
{-# NOINLINE myGlobalVar #-}
myGlobalVar = unsafePerformIO (newIORef 17)