Attempt at Abstracted YAML Configuration Loader I've been playing around with writing a simple static blog generator in Haskell (mostly for experience, since there's probably more than a few already made choices that fit my needs). I don't have very much experience with functional programming, but I've been doing a lot of C++ lately, so using typeclasses as "interfaces" seems natural to me. 

For the beginnings configuration, I've come up with this typeclass:

module BlogGenerator.Config where

class Config t where
    outputPath      :: t -&gt; FilePath
    templatePathFor :: String -&gt; t -&gt; FilePath


And for a YAML implementation (using this):

module BlogGenerator.YamlConfig 
    ( YamlConfig
    , toYamlValue
    , toYamlConfig
    ) where

import qualified Data.HashMap.Strict as HashMap
import Data.Maybe
import Data.ByteString
import Data.Text as Text
import BlogGenerator.Config
import qualified Data.Yaml as Yaml

newtype YamlConfig = YamlConfig
    { toYamlValue :: Yaml.Value
    }

toYamlConfig :: ByteString -&gt; YamlConfig
toYamlConfig = YamlConfig . fromJust . decode
  where
    decode = (Yaml.decode :: ByteString -&gt; Maybe Yaml.Value)

instance Config YamlConfig where
    templatePathFor name = stringYamlLookup ["templates", name] . toYamlValue
    outputPath = stringYamlLookup ["output", "path"] . toYamlValue

-- helpers
yamlLookup :: [String] -&gt; Yaml.Value -&gt; Yaml.Value
yamlLookup []         _     = Yaml.Null
yamlLookup (key:[])   value = fromJust $ look key $ toObject value
  where
    look k v = HashMap.lookup (Text.pack k) v
    toObject = fromJust . toOptionalObject
    toOptionalObject (Yaml.Object object) = Just object
    toOptionalObject _                    = Nothing
yamlLookup (key:rest) value = yamlLookup rest (yamlLookup [key] value)

stringYamlLookup :: [String] -&gt; Yaml.Value -&gt; String
stringYamlLookup ks v = Text.unpack (toText (yamlLookup ks v))
  where
    toText = fromJust . toOptionalText
    toOptionalText (Yaml.String text) = Just text
    toOptionalText _ = Nothing


I know the YAML package contains support for automagic binding to records, but tight coupling between the external format and the internal format is fairly unappealing to me, and before I start trusting advanced code like that I feel better about writing a "low-tech" version of my own.

Is this general approach "idiomatic" for Haskell, and am I following good practices/idioms in general?
Sorry to say this is not good practice. Exactly why is a bit more technical of an explanation than I like to give off the cuff, Google for "Haskell typeclass interface" and you'll find a bunch of threads comparing and contrasting and recommending you use an ADT. Here's one from Reddit with a game programming example that I think is particularly practical.

In your case, I imagine you intend to provide different configuration methods (XML, command line flags, &amp;c) which would also be instances of Config. You've already realized that you want to work with some abstract Config thing elsewhere in your program and not a YamlConfig, because whatever is using a configuration value doesn't care where that value came from. The next logical step is that by making Config a class, you actually are still passing a YamlConfig around everywhere, you're just relying on callers to never look more closely at it than as a generic Config instance. You've got implementation leaking all over the place.

Instead, making Config a datatype.

data Config = Config { outputPath :: FilePath
                     , template   :: String -&gt; FilePath
                     }


Now populate it from a YAML source.

makeConfig :: Yaml.Value -&gt; Config
makeConfig yaml = Config { outputPath = stringYamlLookup ["output", "path"] yaml
                         , template   = \name -&gt; stringYamlLookup ["templates", name] yaml
                         }


It's easy to make arbitrary Configs now. Maybe you want a default Config.

defaultConfig :: Config
defaultConfig = Config { outputPath = "output/"
                       , template   = ("template/" ++)
                       }


You can even make lists of Configs from different sources ([defaultConfig, makeConfig yaml]).