2015-05-09 54 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