问题
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:
- How does
readArgs
work? - 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