4

I've been trying to build a command line parser with Turtle, nothing fancy: https://github.com/Tyrn/go-procr

#!/usr/bin/env stack
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Turtle
import Prelude hiding (FilePath)

parserSwitch :: Parser (Bool, Bool)
parserSwitch = (,) <$> switch "verbose" 'v' "Unless verbose, just progress bar is shown"
                   <*> switch "filetitle" 'f' "Use file name for title tag"

parserArg :: Parser (FilePath, FilePath)
parserArg = (,)    <$> argPath "src" "Source directory"
                   <*> argPath "dst" "Destination directory"

main :: IO ()
main = do
  (verbose, filetitle) <- options "Flags" parserSwitch
  echo (format ("verbose: "%w) verbose)
  echo (format ("filetitle: "%w) filetitle)
  (src, dst) <- options "Args" parserArg
  echo (format ("src: "%fp) src)
  echo (format ("dst: "%fp) dst)

There are three kinds of arguments required: boolean flags; options, text and integer; positional arguments. So far I got stuck at boolean flags and positional arguments. Unfortunately, the examples seem to be too basic even for this.

  1. Do I really have to build separate parsers for different kinds of options (I did not manage to satisfy syntax with a single parser)?

  2. It won't work as expected, anyway.

I can't figure out what my next step should be.

1 Answer 1

4

Your first step is to have something where you can store and retrieve your options easily:

data Settings = Settings
   { settingsVerbose      :: Bool
   , settingsFileTitle    :: Bool
   , settingsSource       :: FilePath
   , settingsDestination  :: FilePath
   }

Afterwards, you write parsers for your options. To make things clear, let's be a little bit verbose first:

verboseParser :: Parser Bool
verboseParser = switch "verbose" 'v' "Be more verbose"

fileTitleParser :: Parser Bool
fileTitleParser = switch "filetitle" 'f' "..."

sourceParser :: Parser FilePath
sourceParser = argPath "src" "Source directory"

destinationParser :: Parser FilePath
destinationParser = argPath "dst" "Destination directory"

Since Parser is an instance of Applicative we can then combine all options in a single parser:

settingsParser :: Parser Settings
settingsParser = 
    Settings <$> verboseParser
             <*> fileTitleParser
             <*> sourceParser
             <*> destinationParser

We've combined all four parsers into a single parser, similar to the combination via (,). Now we can parse the options with a single call to options. After all, either all arguments are correct, or we have to present the user the proper usage:

main = do
   s <- options "Description of your program" settingsParser

   echo (format ("verbose: "%w)   (settingsVerbose s))
   echo (format ("filetitle: "%w) (settingsFileTitle s))
   echo (format ("src: "%fp)      (settingsSource s))
   echo (format ("dst: "%fp)      (settingsDestination s))

You probably want to use shorter names, though, and maybe write the parsers in settingsParser:

data Settings = Settings
   { sVerbose     :: Bool
   , sFileTitle   :: Bool
   , sSource      :: FilePath
   , sDestination :: FilePath
   }

settingsP :: Parser Settings
settingsP = 
  Settings <$> switch "verbose"   'v' "Be more verbose"
           <*> switch "filetitle" 'f' "..."
           <*> argPath "src" "Source directory"
           <*> argPath "dest" "Destination directory"

description :: Description
description = "Description of your program"

main = do
  (Settings verbose filetitle source dest) <- options description settingsP
  ...
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much, Zeta! The settingsP approach is excellent.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.