2015-05-09 92 views
6

我正在使用haskell项目,其中的设置当前位于名为Setting.hs的文件中,因此它们在编译期间被检查并且可以在全局访问。从静态配置移动到动态配置

但是,由于这有点过于静态,我正考虑在运行时读取配置。代码库是巨大的,看起来要通过设置是相当大的努力,例如作为整个程序流程的一个参数,因为它们可以从任何地方随意访问。

是否有任何设计模式,库或ghc扩展可以在不重构整个代码的情况下提供帮助?

+0

隐式参数或读者monad是常用选项,但它们确实需要一些更改。 – chi

+1

看看[隐式配置 - 或者,类型类反映类型的值](http://okmij.org/ftp/Haskell/types.html#Prepose),如果它可以帮助。 –

+2

在@PetrPudlák注释中进行了扩展,隐式配置的实现可以在'reflection'包中找到。在repo的examples文件夹中,有一个看起来很相关的“类似读者”的例子:https://github.com/ekmett/reflection/blob/master/examples/ReaderLike.hs。另请参阅此SO回答以获取使用示例:http://stackoverflow.com/a/29929718/1364288 – danidiaz

回答

0

你在问什么,如果可能会破坏参照透明度,至少对于纯函数(纯函数结果可能取决于某些全局变量,但不能在配置文件上不能))?

通常人们通过Monad隐式传递配置作为数据来避免这种情况。或者(如果你很乐意重构你的代码),你可以使用implicit parameter extenson,这在理论上已经被用来解决这种类型的问题,但实际上并不真正起作用。 但是,如果你真的需要,你可以使用unsafePerformIOioRef有一个top level mutable state这是肮脏和皱眉咆哮。你需要一个最高级别的可变状态,因为当你加载它时,你需要修改“mutate”你的初始配置。

然后你得到这样的事情:

myGlobalVar :: IORef Int 
{-# NOINLINE myGlobalVar #-} 
myGlobalVar = unsafePerformIO (newIORef 17) 
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(获取值)的函数似乎不需要类约束。这意味着我只需要改变实际访问全局配置的功能。

这就是我一直在寻找的。

+0

它与隐式参数有什么不同。如果'f'调用需要约束的'g','f'是否也需要约束?如果不是,'g'是否会得到价值? – mb14

+0

是的,'f'也需要约束。我已经更新了代码。另外,你的问题非常好,我只能模糊地回答它。但是反思基于http://okmij.org/ftp/Haskell/tr-15-04.pdf的论文提到了引言和6.2节中的“隐含参数”的几个优点。反射应该与类型系统“更好地”发挥作用,并且更好地支持并发的参数集。 – hasufell

+0

'give'被认为是邪恶的。爱德华Kmett已经表示,如果其他人不会反对如此强烈,他会完全删除它。 – dfeuer