假设我有一个Data.Dynamic.Dynamic对象,它包装了一个IO动作(也就是某些类型IO a可能未知的类型a).我觉得我应该能够执行这个IO操作并获得它的结果,包含在Dynamic(它将具有类型a)中.有没有标准的库函数可以做到这一点?(有点像dynApply,但是对于IO动作性能而不是函数应用程序.)
该函数的实现可能看起来像
dynPerform :: Dynamic -> Maybe IO Dynamic
dynPerform (Dynamic typ act)
= if (typeRepTyCon typ) /= ioTyCon then Nothing else Just $
do result <- (unsafeCoerce act :: IO Any)
return Just . Dynamic (head $ typeRepArgs typ) $ result
exampleIOAction = putChar
typeOfIOAction = typeOf exampleIOAction
ioTyCon = typeRepTyCon typeOfIOAction
Run Code Online (Sandbox Code Playgroud)
但显然这是使用几个不安全的操作,所以我宁愿从库中取出它.(事实上,由于Data.Dynamic.Dynamic类型的不透明性,我所编写的内容在Data.Dynamic之外不起作用.)
我不相信你可以安全地做你想做的事。让我建议一种替代方法。
也许幻象类型可以在这里帮助你。假设您正在提供某种 cron 作业服务,用户让您每x微秒执行一个操作,并且用户可以随时查询以查看该操作最近运行的结果。
假设您自己可以访问以下原语:
freshKey :: IO Key
save :: Key -> Dynamic -> IO ()
load :: Key -> IO (Maybe Dynamic)
Run Code Online (Sandbox Code Playgroud)
您应该安排作业并制定存储结果的计划,同时您仍然在类型系统中“知道”操作的类型。
-- do not export the internals of PhantomKey
data PhantomKey a = PhantomKey {
getKey :: Key
getThread :: Async ()
}
-- This is how your user acquires phantom keys;
-- their phantom type is tied to the type of the input action
schedule :: Typeable a => Int -> IO a -> IO (PhantomKey a)
schedule microseconds m = do
k <- freshKey
let go = do
threadDelay microseconds
a <- m
save k (toDyn a)
go
thread <- async go
return $ PhantomKey k thread
unschedule :: PhantomKey a -> IO ()
unschedule pk = cancel (getThread pk)
-- This is how your user uses phantom keys;
-- notice the function result type is tied to the phantom key type
peekLatest :: PhantomKey a -> IO (Maybe a)
peekLatest pk = load (getKey pk) >>= \md -> case md of
Nothing -> return Nothing -- Nothing stored at this key (yet?)
Just dyn -> case fromDynamic dyn of
Nothing -> return Nothing -- mismatched data type stored at this key
-- hitting this branch is probably a bug
Just a -> return (Just a)
Run Code Online (Sandbox Code Playgroud)
现在,如果我是您的 API 的用户,我可以将它与您一无所知的我自己的数据类型一起使用,只要它们是可输入的:
refreshFoo :: IO Foo
main = do
fooKey <- schedule 1000000 refreshFoo
-- fooKey :: PhantomKey Foo
mfoo <- peekLatest fooKey
-- mfoo :: Maybe Foo
Run Code Online (Sandbox Code Playgroud)
那么我们取得了什么成就呢?
所有这些都不需要您的库了解有关用户数据类型的任何信息。
在我看来,如果您将已知的IO操作放入动态 blob 中,则在您应该使用所述类型信息的上下文中,您已经在类型系统中丢失了有关该事物的信息。TypeRep 可以为您提供值级别的类型信息,但(据我所知)无法将该信息返回到类型级别。