Глобальные переменные через unsafePerformIO в Haskell - PullRequest
3 голосов
/ 27 июня 2011

GHC API требует, чтобы некоторая инициализация произошла до вызова. В частности, parseStaticFlags может быть вызван только один раз.

У меня есть функции, которые могут вызывать runGhc :: MaybeFilePath :: Ghc a -> IO a несколько раз для запуска некоторых методов API GHC. Однако некоторая инициализация должна происходить только при первом вызове функции.

Кажется, я помню из Yi источника, что можно создать глобальную переменную, похожую на

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

так, чтобы в монадическом действии, которое вызывает runGhc, мы могли иметь

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

Однако я не могу вспомнить, как именно это делается. Этот код находится в функции runMonad для монады, которая заключает в себе GhcMonad. Мне хорошо известно, что использование 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)

Ответы [ 2 ]

2 голосов
/ 28 июня 2011

Вам нужно отключить встраивание.Другая важная вещь: тип должен быть мономорфным (= без переменных типа), иначе вы можете оценить unsafePerformIO для каждого фактического типа.

{-# NOINLINE ghcInitialised #-}
ghcInitialised :: MVar (Bool,[String])
ghcInitialised = unsafePerformIO $ newMVar (False,[])
1 голос
/ 27 июня 2011

См. этот ответ .Он показывает, как использовать глобальный счетчик, который «тикает» каждый раз, когда вы на него смотрите.Счетчик вам не нужен, но вместо +1 вы просто помещаете в него True.

Или, что еще лучше, вы помещаете код инициализации в unsafePerformIO, (охраняемыйif конечно).

...