2012/9/28 Iustin Pop <[email protected]>:
> +-- | Prefixes a path with the current root directory
> +addNodePrefix :: FilePath -> FilePath
> +addNodePrefix path = do
> +  let val = unsafePerformIO $ getEnv "GANETI_ROOTDIR"
> +  case val of
> +    Just rootdir -> rootdir ++ path
> +    Nothing -> path
> +
>
> And there are a few problems with it.
>
> Generally speaking, there are two ways to use unsafePerformIO:
>
> 1) however you want, if you are a wizard and know what you're doing
> 2) by following a strict set of rules, otherwise
>
> […]

Thanks for the explanation!

> So in this case, the proper way to write this would be:
>
> {-# NOINLINE getRootDir #-}
> getRootDir :: FilePath
> getRootDir = unsafePerformIO $ getEnv "GANETI_ROOTDIR"
>
> so that this function/value is evaluated only once, and then use that in
> addNodePrefix (which would be a honest pure function).

Are there any guarantees it's only evaluated once?

> That was about unsafePerformIO. However, there are two other issues.
> First, this does not catch any exceptions, and basically requires the
> variable to be defined.

System.Posix.Env.getEnv/getEnvDefault don't raise exceptions. The
former returns a Maybe, the latter even takes a default value (only
discovered this today).

> Second, I'm not sure whether we actually need to use unsafePerformIO,
> instead of making the node daemon log files functions that take the root
> path with, reading the environment nicely in the daemon startup.

I have to say this is beyond my Haskell abilities. I'm including an
interdiff below which separates unsafePerformIO into a separate
function and makes use of getEnvDefault. How about me committing this
and you can then change it?

--- a/htools/Ganeti/Path.hs
+++ b/htools/Ganeti/Path.hs
@@ -27,17 +27,16 @@ module Ganeti.Path where

 import qualified Ganeti.Constants as C
 import System.FilePath
-import System.Posix.Env (getEnv)
+import System.Posix.Env (getEnvDefault)
 import System.IO.Unsafe

+{-# NOINLINE getRootDir #-}
+getRootDir :: FilePath
+getRootDir = unsafePerformIO $ getEnvDefault "GANETI_ROOTDIR" ""

 -- | Prefixes a path with the current root directory
 addNodePrefix :: FilePath -> FilePath
-addNodePrefix path = do
-  let val = unsafePerformIO $ getEnv "GANETI_ROOTDIR"
-  case val of
-    Just rootdir -> rootdir ++ path
-    Nothing -> path
+addNodePrefix path = getRootDir ++ path

 -- | Directory for data
 dataDir :: FilePath


Michael

Reply via email to