Make ReadArgs 1.0 work with a single argument

佐手、 提交于 2020-01-03 14:00:39

问题


Playing around with the ReadArgs package, it seems that it does not support single-argument situations.

{-# LANGUAGE ScopedTypeVariables #-}

import ReadArgs (readArgs)

main = do
  (foo :: Int) <- readArgs
  print foo

The error is (when using version 1.0):

No instance for (ReadArgs.ArgumentTuple Int)
  arising from a use of `readArgs'

My question is twofold:

  1. How does readArgs work?
  2. How can that library be adjusted to allow it to work with a single argument as well?

N.B. version 1.1 of ReadArgs eliminates this "error"; see comments.


回答1:


I don't quite understand all of the extensions I needed to enable, but you could define an instance of ReadArgs.ArgumentTuple a (even though it's not really a semantically correct name) like this:

{-# LANGUAGE FlexibleInstances, UndecidableInstances,
             OverlappingInstances, ScopedTypeVariables #-}

import ReadArgs

instance (Argument a) => ArgumentTuple a where
  parseArgsFrom ss = do
    a :& () <- parseArgsFrom ss
    return a
  usageFor a = usageFor (a :& ())

main = do
    (foo :: Int) <- readArgs
    print foo

Also, I'm not really sure if there are any problems with this instance, though it works for the example you presented. I would assume there's a reason it's missing from the library, but I may be wrong.

Update

After looking trying a few things out, to be sure they still work (like the following example), I'm fairly convinced this doesn't cause any problems, so maybe it's (or something similar) existance was just an oversight.

main = do
    (foo :: Int, bar :: Int) <- readArgs
    print foo
    print bar



回答2:


From what I can tell, the package uses tuples to emulate type-safe heterogeneous lists. As you noticed, this causes problems when you want only one argument, as there are no one-tuples in Haskell.

However, the package also provides a proper type for heterogeneous lists, which can be used instead of tuples: the :& type. You use it similar to the : operator, with the empty tuple () as a terminator:

(foo :: Int) :& (bar :: String) :& () <- readArgs

This works trivially with one argument:

(foo :: Int) :& () <- readArgs



回答3:


Another answer, just for fun: reifying Daniel Wagner's suggestion of adding an instance for OneTuple:

{-# LANGUAGE ScopedTypeVariables #-}

import ReadArgs
import Data.Tuple.OneTuple

instance (Argument a) => ArgumentTuple (OneTuple a) where
  parseArgsFrom ss = do
    a :& () <- parseArgsFrom ss
    return $ OneTuple a
  usageFor (OneTuple a) = usageFor (a :& ())

main = do
    OneTuple (foo :: Int) <- readArgs
    print foo

Mostly stolen from Adam Wagner's solution. Amazingly, all the extra language pragmas can be removed.

This may sound silly, but the OneTuple (foo :: Int) <- readArgs isn't nearly as ugly as I imagined it would be for some reason.




回答4:


One retarded solution is to abuse the "optional argument" feature:

(foo :: Int, _ :: Maybe ()) <- readArgs

This silently works even if you supply () as the second arg:

$ runhaskell args.hs 3 ()
3

And screws up the usage message a little:

$ runhaskell args.hs 3 foo
usage: args.hs Int [()]

But it does reject extra arguments that are not (), and it does work as desired:

$ runhaskell args.hs 3
3


来源:https://stackoverflow.com/questions/8752532/make-readargs-1-0-work-with-a-single-argument

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!