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
IO. Both of these drawbacks are not that critical, but calling all
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:
( MonadIO m, IsString a ) => Text -> m (Maybe a)- a wrapper for
lookupEnv. Aside from working with
Textinput it also lives in
MonadIOand returns an
IsString, which the most interesting part here. I will talk about it shortly.
( MonadThrow m, MonadIO m, IsString a ) => Text -> m a- the unsafe version of
( 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
getEnv depending on my
desire to handle missing variable.
> envMaybe "NAME" Nothing > setEnv "NAME" "Boris" > envMaybe "NAME" Just "Boris"
envMaybe is polymorphic in it’s return type, which means
that it plays nicely in composition chains and you don’t need to explicitly
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
> 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!