Proper way to treat global flags in Haskell

后端 未结 4 787
天涯浪人
天涯浪人 2021-02-07 12:19

I often need to make a core function that\'s used in many places somehow configurable - i.e., it may use either algorithm A or algorithm B depending on a command-line switch; o

4条回答
  •  傲寒
    傲寒 (楼主)
    2021-02-07 13:09

    These days, I prefer to use a Reader monad to structure the read-only state of the application. The environment is initalized at startup, and then available throughout the top level of the program.

    An example is xmonad:

    newtype X a = X (ReaderT XConf (StateT XState IO) a)
        deriving (Functor, Monad, MonadIO, MonadReader XConf)
    

    The top level parts of the program run in X instead of IO; where XConf is the data structure initalized by command line flags (and environment variables).

    The XConf state can then be passed as pure data to the functions that need it. With newtype deriving you also get to reuse all the MonadReader code for accessing state.

    This approach retains the semantic purity of 2. but gives you less code to write, as the monad does the plumbing.

    I think its the "true" Haskell way to do read-only config state.

    --

    The approaches that use unsafePerformIO to initialize global state also work, of course, but tend to bite you eventually (e.g. when you make your program concurrent or parallel). They also have funny initialization semantics.

提交回复
热议问题