如何将 IO monad 中的值分配给 RankNType 合格的构造函数
How to assign a value from the IO monad to a RankNType qualified constructor
(已更新)
我使用 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
[...]
更新
我最初的最小示例是最小的。一些进一步的实验表明,当我需要在 IO monad 中加载接口以便读取命令行选项时,问题就出现了。我已经更新了示例以包含该问题。知道了这一点,我也许可以围绕它编写代码。
有趣的 GHCI 告诉我类型 IO (DataStore a -> IO a)
的函数的结果是 DataStore GHC.Prim.Any -> IO GHC.Prim.Any
这不是我所期望的。
这里的问题是
pickStorageInterface :: forall a. IO (DataStore a -> IO a)
虽然我们需要(命令式)类型
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}
...
(已更新)
我使用 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
[...]
更新
我最初的最小示例是最小的。一些进一步的实验表明,当我需要在 IO monad 中加载接口以便读取命令行选项时,问题就出现了。我已经更新了示例以包含该问题。知道了这一点,我也许可以围绕它编写代码。
有趣的 GHCI 告诉我类型 IO (DataStore a -> IO a)
的函数的结果是 DataStore GHC.Prim.Any -> IO GHC.Prim.Any
这不是我所期望的。
这里的问题是
pickStorageInterface :: forall a. IO (DataStore a -> IO a)
虽然我们需要(命令式)类型
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}
...