Haskell 中通过 unsafePerformIO 的全局变量

发布于 2024-11-17 01:36:56 字数 1190 浏览 1 评论 0原文

GHC API 要求在调用之前进行一些初始化。具体来说,parseStaticFlags 只能调用一次。

我有可以调用 runGhc :: MaybeFilePath :: Ghc a -> 的函数多次 IO a 来运行某些 GHC API 方法。然而,某些初始化应该只在第一次调用该函数时发生。

我似乎记得从 Yi 源中可以创建一个全局变量,

ghcInitialised :: MVar (Bool,[String])
ghcInitialised = unsafePerformIO $ newMVar (False,[])

这样在调用 runGhc 的单子操作中我们就可以拥有

(init,flags) <- readMVar ghcInitialised
when (not init) $ do
   ...
   (_,_,staticFlagWarnings) <- parseStaticFlags ...
   ...
   putMVar ghcInitialised (True,staticFlagWarnings)

但是,我不记得了到底是如何完成的。此代码位于包装 GhcMonad 的 monad 的 runMonad 函数中。我很清楚使用 unsafePerformIO 不是纯粹的或功能性的,但(在当时)这是实现实际结果的最佳方式。

[编辑:工作解决方案:

{-# NOINLINE ghcInitialised #-}
ghcInitialised :: MVar (Bool,[String])
ghcInitialised = unsafePerformIO $ newMVar (False,[])

以便在调用 runGhc 的单子操作中,我们可以

(init,flags) <- takeMVar ghcInitialised
when (not init) $ do
   ...
   (_,_,staticFlagWarnings) <- parseStaticFlags ...
   ...
   putMVar ghcInitialised (True,staticFlagWarnings)

The GHC API requires that some initialisation occurs before invocation. Specifically, parseStaticFlags can only be called once.

I have functions that can call runGhc :: MaybeFilePath :: Ghc a -> IO a multiple times to run some GHC API methods. However, some of that initialisation should only occur the first time that function is called.

I seem to remember from Yi source that it is possible to create a global variable something like

ghcInitialised :: MVar (Bool,[String])
ghcInitialised = unsafePerformIO $ newMVar (False,[])

so that in the monadic action that calls runGhc we can have

(init,flags) <- readMVar ghcInitialised
when (not init) $ do
   ...
   (_,_,staticFlagWarnings) <- parseStaticFlags ...
   ...
   putMVar ghcInitialised (True,staticFlagWarnings)

However, I can not recall exactly how it is done. This code is in the runMonad function for the monad that wraps a GhcMonad. I am well aware that using unsafePerformIO is not pure or functional, but (at the time) this was the best way of achieving a practical result.

[Edit: the working solution:

{-# NOINLINE ghcInitialised #-}
ghcInitialised :: MVar (Bool,[String])
ghcInitialised = unsafePerformIO $ newMVar (False,[])

so that in the monadic action that calls runGhc we can have

(init,flags) <- takeMVar ghcInitialised
when (not init) $ do
   ...
   (_,_,staticFlagWarnings) <- parseStaticFlags ...
   ...
   putMVar ghcInitialised (True,staticFlagWarnings)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

笑看君怀她人 2024-11-24 01:36:56

您需要关闭内联。
另一件重要的事情:类型必须是单态的(=没有类型变量),否则你可能会为每个实际类型评估 unsafePerformIO 。

{-# NOINLINE ghcInitialised #-}
ghcInitialised :: MVar (Bool,[String])
ghcInitialised = unsafePerformIO $ newMVar (False,[])

You need to turn inlining off.
The other important thing: The type has to be monomorph (= no type variables), otherwise you might evaluate unsafePerformIO for each actual type.

{-# NOINLINE ghcInitialised #-}
ghcInitialised :: MVar (Bool,[String])
ghcInitialised = unsafePerformIO $ newMVar (False,[])
卸妝后依然美 2024-11-24 01:36:56

请参阅此答案。它展示了如何使用每次查看时都会“滴答作响”的全局计数器。您不需要计数器,但只需将 True 放入其中即可,而不是 +1

或者,更好的是,您将初始化代码放入 unsafePerformIO 中(当然由 if 保护)。

See this answer. It shows how to use a global counter that 'ticks' everytime you look at it. You don't need a counter, but instead of +1, you just put True into it.

Or, even better, you put the initialisation code into the unsafePerformIO, (guarded by an if of course).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文