FP Complete

Section exercise: write a program using the rio template that receives a database connection string as an environment variable, by using a YAML config file.

Basic usage

#!/usr/bin/env stack
-- stack --resolver lts-12.21 script
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson (withObject) -- should be provided by yaml...
import Data.Text (Text)
import Data.Vector (Vector)
import Data.Yaml

data Person = Person
  { personName :: !Text
  , personAge :: !Int
  }
  deriving (Show, Eq)
-- Could use Generic deriving, doing it by hand
instance ToJSON Person where
  toJSON Person {..} = object
    [ "name" .= personName
    , "age"  .= personAge
    ]
instance FromJSON Person where
  parseJSON = withObject "Person" $ o -> Person
    <$> o .: "name"
    <*> o .: "age"

main :: IO ()
main = do
  let bs = encode
        [ Person "Alice" 25
        , Person "Bob" 30
        , Person "Charlie" 35
        ]
  people <-
    case decodeEither' bs of
      Left exc -> error $ "Could not parse: " ++ show exc
      Right people -> return people

  let fp = "people.yaml"
  encodeFile fp (people :: Vector Person)
  res <- decodeFileEither fp
  case res of
    Left exc -> error $ "Could not parse file: " ++ show exc
    Right people2
      | people == people2 -> mapM_ print people
      | otherwise -> error "Mismatch!"

Config files

YAML file itself

aws-secret: _env:AWS_SECRET
home-response: _env:HOME_RESPONSE:Hello World

Haskell code

#!/usr/bin/env stack
-- stack --resolver lts-12.21 script
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson (withObject)
import Data.Text (Text)
import Data.Yaml
import Data.Yaml.Config

data Config = Config
  { awsSecret    :: !Text
  , homeResponse :: !Text
  }
  deriving Show
instance FromJSON Config where
  parseJSON = withObject "Config" $ o -> Config
    <$> o .: "aws-secret"
    <*> o .: "home-response"

main :: IO ()
main = do
  config <- loadYamlSettingsArgs [] useEnv
  print (config :: Config)

Usage

$ ./Main.hs
Main.hs: loadYamlSettings: No configuration provided
$ ./Main.hs config.yaml
Main.hs: Could not convert to AppSettings: expected Text,
         encountered Null
$ AWS_SECRET=foobar ./Main.hs config.yaml
Config {awsSecret = "foobar", homeResponse = "Hello World"}
$ AWS_SECRET=foobar HOME_RESPONSE=Goodbye ./Main.hs config.yaml
Config {awsSecret = "foobar", homeResponse = "Goodbye"}

Exercises

Write a Haskell program to generate the following YAML file:

- title: Star Wars
  director: George Lucas
- title: Transformers
  director: Michael Bay

Write a Haskell program to convert a JSON formatted file to YAML format, and vice-versa.

Subscribe to our blog via email

Email subscriptions come from our Atom feed and are handled by Blogtrottr. You will only receive notifications of blog posts, and can unsubscribe any time.