env-extra v1.0.0.0
It’s hard (though possible) to imagine a script that doesn’t access environment variables. In Haskell ecosystem there is a good built-in module System.Environment that does the job, but it has several drawbacks. First of all, it uses String
data type as input and as an output. And secondly, it lives in IO
. Both of these drawbacks are not that critical, but calling all these Text.pack
, Text.unpack
and liftIO
in every program drives me nuts. So several years ago (somewhere in 2016) I decided to write a simple library that wraps it for me.
I’ve been using this library for a long time and today I uploaded it to Hackage. While it’s really small, I still think that some of you might find it useful, because it has nice features that original System.Environment
doesn’t have. Let’s take a look!
So in the nutshell, there are three functions to access environment variables:
envMaybe
of type( MonadIO m, IsString a ) => Text -> m (Maybe a)
- a wrapper forlookupEnv
. Aside from working withText
input it also lives inMonadIO
and returns anIsString
, which the most interesting part here. I will talk about it shortly.getEnv
of type( MonadThrow m, MonadIO m, IsString a ) => Text -> m a
- the unsafe version ofenvMaybe
.envRead
of type( MonadIO m ) => Reader a -> Text -> m (Maybe a)
- a helper function that also reads (or parses) a value from environment variable by specifying the Reader.
In most cases my choice is between envMaybe
and getEnv
depending on my desire to handle missing variable.
> envMaybe "NAME"
Nothing
> setEnv "NAME" "Boris"
> envMaybe "NAME"
Just "Boris"
What’s interesting, envMaybe
is polymorphic in it’s return type, which means that it plays nicely in composition chains and you don’t need to explicitly convert between Text
, String
, ByteString
data types.
> :t putStrLn
putStrLn :: String -> IO ()
> getEnv "NAME" >>= putStrLn
Boris
> setEnv "SOME_VAR" "NAME"
> getEnv "SOME_VAR"
"NAME"
> getEnv "SOME_VAR" >>= getEnv
"Boris"
The other function that I use (though not that often) is envRead
.
> setEnv "AGE" "10"
> envRead decimal "AGE"
Just 10
> envRead hexadecimal "AGE"
Just 16
> envRead hexadecimal "HOME"
Nothing
In some rare cases you might want to use the Read
instance for parsing. Though
it’s not advisable.
> data Status = SomeStatus1 | SomeStatus2 deriving (Show, Read, Eq)
> getEnv "STATUS"
"SomeStatus2"
> envRead read "STATUS" :: IO (Maybe Status)
Just SomeStatus2
I hope you’ll find this library helpful. Please do send me your comments, idea and thoughts! And any contribution is welcome!