env-extra v1.0.0.0

January 6, 2020

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 for lookupEnv. Aside from working with Text input it also lives in MonadIO and returns an IsString, 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 of envMaybe.
• 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"

Just 10

Just 16

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)
Just SomeStatus2