2016-07-29 26 views
1

(修訂版)如何從IO單子的值ASIGN到RankNType合格構造

我已使用Free Monad到一個通用的數據存儲的接口。我想將運行時用戶選擇的特定解釋器(:: DataStore a - > IO a)與其他一些信息一起放入狀態monad中。我似乎無法將任何東西放入數據結構的這個字段中。

我如何把一個值定義爲較高級別類型的字段?

下面是一個最小的例子:

{-# LANGUAGE RankNTypes, DeriveFunctor #-} 

data ProgramState = PS { -- line 3 
    [...] 
    , storageInterface :: (forall a. DataStore a -> IO a) 
    } 

data DataStoreF next = 
    Create Asset       (String -> next) 
    | Read  String       (Asset -> next) 
    | Update Asset       (Bool -> next) 
    | UpdateAll [Asset]       (Bool -> next) 
    | [...] 
    deriving Functor 

type DataStore = Free DataStoreF 

runMemory :: (IORef (Map String Asset)) -> DataStore a -> IO a 
runMemory ms (Pure a) = return a 
runMemory ms (Free Create asset next) = [...] 
runMemory ms (Free Read str next) = [...] 
[...] 

pickStorageInterface :: IO (DataStore a -> IO a) 
pickStorageInterface = do 
    opts <- parseOptions 
    case (storage opts) of 
    MemoryStorage -> 
     ms <- readAssetsFromDisk 
     return $ runMemory ms 
    SomeOtherStorage -> [...] 

restOfProgram :: StateT ProgramState IO 
restOfProgram = [...] 

main = do 
    si <- pickStorageInterface 
    let programState = PS { storageInterface = si} -- line 21 
    evalState restOfProgram programState 

當我嘗試這樣做GHC抱怨說:

Main.hs: << Line 21 >> 
Couldn't match type `a0' with `a' 
    because type variable `a' would escape its scope 
This (rigid, skolem) type variable is bound by 
    a type expected by the context: DataStore a -> IO a 
    at Main.hs <<line 3>> 
Expected type: DataStore a -> IO a 
    Actual type: DataStore a0 -> IO a0 
In the `storageInterface' field of a record 
    [...] 

UPDATE

我原來的小例子,是最小的。一些進一步的實驗表明,當我需要在IO monad中加載接口時出現問題,所以我可以閱讀命令行選項。我已更新示例以包含該問題。知道這一點,我可能能夠圍繞它進行編碼。

有趣的GHCI告訴我,IO (DataStore a -> IO a)型函數的結果是DataStore GHC.Prim.Any -> IO GHC.Prim.Any,這不是我所期望的。

+0

也許我缺少明顯的東西,但你爲什麼不只是做'數據ProgramState A = {PS .. 。,storageInterface :: DataStore a - > IO a}'?我不認爲這打破了你粘貼在這裏的任何代碼... – Alec

+0

無法複製該問題。 – Alec

+0

Alec'不在範圍內:輸入變量'a' –

回答

2

的這裏的問題是,

pickStorageInterface :: forall a. IO (DataStore a -> IO a) 

,而我們需要的(impredicative)型

pickStorageInterface :: IO (forall a. DataStore a -> IO a) 

對於上面的代碼工作。唉,現在GHC中的暗示類型處於悲傷狀態,最好避免。

您可以解決在使用各地普遍量化類型newtype包裝:

newtype SI = SI { runSI :: forall a. DataStore a -> IO a } 

pickStorageInterface :: IO SI 
pickStorageInterface = do 
    opts <- parseOptions 
    case (storage opts) of 
    MemoryStorage -> 
     ms <- readAssetsFromDisk 
     return $ SI $ runMemory ms 
    ... 

main = do 
    si <- pickStorageInterface 
    let programState = PS { storageInterface = runSI si} 
    ...