How to asign a value from the IO monad to a RankNType qualified constructor

ε祈祈猫儿з 提交于 2019-12-02 13:30:17

问题


(UPDATED)

I have made an interface using a Free Monad to a generic data store. I want to place the specific interpreter (:: DataStore a -> IO a) chosen by the user at run time into a state monad along with some other information. I cannot seem to put anything into this field in the data structure.

How do I put a value into a field defined as a higher rank type?

Below is a minimum example:

{-# 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

When I try to do this GHC complains that:

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

My original minimal example was to minimal. Some further experimentation shows that the problem arises when I need to load the interface in an the IO monad so I can read the command line options. I've updated the example to include that issue. Knowing this I may be able to code around it.

Interesting GHCI tells me that the results of a function of type IO (DataStore a -> IO a) is DataStore GHC.Prim.Any -> IO GHC.Prim.Any which is not what I expected.


回答1:


The issue here is that

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

while we would need the (impredicative) type

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

for the code above to work. Alas, the impredicative types are in a sad state now in GHC, and are best to be avoided.

You can work around that using a newtype wrapper around the universally quantified type:

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}
  ...


来源:https://stackoverflow.com/questions/38648935/how-to-asign-a-value-from-the-io-monad-to-a-rankntype-qualified-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!