Заставить ReadArgs 1.0 работать с одним аргументом

Играя с пакетом ReadArgs, кажется, что он не поддерживает ситуации с одним аргументом.

{-# LANGUAGE ScopedTypeVariables #-}

import ReadArgs (readArgs)

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

Ошибка (при использовании версии 1.0):

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

Мой вопрос двоякий:

  1. Как работает readArgs?
  2. Как можно настроить эту библиотеку, чтобы она могла работать и с одним аргументом?

Н.Б. версия 1.1 ReadArgs устраняет эту «ошибку»; см. комментарии.


person Dan Burton    schedule 06.01.2012    source источник
comment
Похоже, что readArgs ожидает предоставить кортеж. Я не думаю, что у вас может быть кортеж из одного элемента --(1) is just 1--так что, вероятно, проблема в этом.   -  person Tikhon Jelvis    schedule 06.01.2012
comment
@TikhonJelvis да, это действительно проблема. Но я все еще хочу знать, как это работает и как его можно настроить, чтобы учесть ситуации, не связанные с кортежами.   -  person Dan Burton    schedule 06.01.2012
comment
Вероятно, правильным решением будет написать экземпляр для OneTuple.   -  person Daniel Wagner    schedule 06.01.2012
comment
Что бы вы хотели узнать о том, как это работает?   -  person rampion    schedule 06.01.2012
comment
@rampion все и вся. :) Объяснение Хаммара меня удовлетворило, но более глубокие объяснения также приветствуются.   -  person Dan Burton    schedule 06.01.2012
comment
Ну, это началось с того, что мне было интересно, как анализировать аргументы командной строки, затем запустил его через Reddit, который дал мне несколько советов, прежде чем я решил упаковать это вверх. Настоящая хитрость заключалась в том, чтобы выяснить, какая LANGUAGE прагма мне нужна и почему. Я хотел иметь возможность анализировать все типы Readable, но специально обрабатывать String и Char (чтобы не требовалось цитирование).   -  person rampion    schedule 06.01.2012
comment
Что действительно заставило меня осознать, так это то, что мы можем использовать типы как обычный язык ~ кортежи для конкатенации, () для пустых, Либо для чередования, [] для звезды Клини. Затем сигнатура типа становится регулярным выражением, которое используется для разбора аргументов.   -  person rampion    schedule 06.01.2012


Ответы (4)


Я не совсем понимаю все расширения, которые мне нужно было включить, но вы можете определить экземпляр ReadArgs.ArgumentTuple a (даже если это не совсем семантически правильное имя) следующим образом:

{-# 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

Кроме того, я не совсем уверен, есть ли какие-либо проблемы с этим экземпляром, хотя он работает для примера, который вы представили. Я предполагаю, что есть причина, по которой его нет в библиотеке, но я могу ошибаться.

Обновлять

Попробовав несколько вещей, чтобы убедиться, что они все еще работают (например, следующий пример), я совершенно убежден, что это не вызывает никаких проблем, поэтому, возможно, его (или что-то подобное) существование было просто недосмотром.

main = do
    (foo :: Int, bar :: Int) <- readArgs
    print foo
    print bar
person Adam Wagner    schedule 06.01.2012
comment
TypeSynonymInstances и TypeOperators не нужны для вашего фрагмента кода, хотя они используются в ReadArgs.hs. - person Dan Burton; 06.01.2012
comment
Я обновил свой пример... спасибо! Вот откуда я их взял, как вы, наверное, заметили. - person Adam Wagner; 06.01.2012
comment
Я принимаю этот ответ, поскольку он обеспечивает точный синтаксис, который я просил. :) - person Dan Burton; 06.01.2012
comment
Я думаю, что в какой-то момент я пробовал что-то подобное и столкнулся с проблемой перекрывающихся экземпляров, но вы, похоже, доказали, что это работает. Если вы отправите мне запрос на включение, я буду рад интегрировать его в следующую версию пакета. - person rampion; 06.01.2012
comment
@DanBurton: объединен, упакован и выпущен как версия 1.1 - person rampion; 06.01.2012
comment
@DanBurton Ты меня опередил! Ну, по крайней мере, я смог помочь. :) - person Adam Wagner; 07.01.2012
comment
@AdamWagner извините, я не смог устоять xD Мой гит-фу слаб, поэтому я хотел попрактиковаться. Мой первый пулреквест! :) - person Dan Burton; 07.01.2012
comment
@DanBurton Не беспокойтесь .. спасибо, что поддержали меня в запросе на вытягивание. - person Adam Wagner; 07.01.2012

Насколько я могу судить, пакет использует кортежи для эмуляции гетерогенных списков, безопасных для типов. Как вы заметили, это вызывает проблемы, когда вам нужен только один аргумент, поскольку в Haskell нет кортежей из одного элемента.

Однако пакет также предоставляет подходящий тип для разнородных списков, который можно использовать вместо кортежей: тип :&. Вы используете его аналогично оператору : с пустым кортежем () в качестве терминатора:

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

Это работает тривиально с одним аргументом:

(foo :: Int) :& () <- readArgs
person hammar    schedule 06.01.2012
comment
хотя определение экземпляра для OneTuples в любом случае может иметь смысл. - person Tikhon Jelvis; 06.01.2012
comment
Я тоже должен был это заметить, поскольку я добавил, что в случае, который я определил в своем ответе: a :& () <- parseArgsFrom ss - person Adam Wagner; 06.01.2012

Другой ответ, просто для удовольствия: повторение предложения Даниэля Вагнера о добавлении экземпляра для 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

В основном украдено из решения Адама Вагнера. Удивительно, но все лишние языковые прагмы можно удалить.

Это может звучать глупо, но OneTuple (foo :: Int) <- readArgs не так уродлив, как я себе представлял по какой-то причине.

person Dan Burton    schedule 06.01.2012
comment
+1 за избавление от языковых прагм. Кроме того, есть ли веская семантическая причина для использования OneTuple? Я не уверен, что у меня есть мнение по этому поводу, мне просто любопытно. - person Adam Wagner; 06.01.2012
comment
Зачем нужна прагма ScopedTypeVariables? - person is7s; 06.01.2012
comment
@is7s do OneTuple (foo :: Int) <- readArgs; ... - это сахар для readArgs >>= (\(OneTuple (Foo :: Int)) -> ...), который, по-видимому, требует ScopedTypeVariables для аннотации этого типа. Я не знаю почему, но GHC кричит на меня, если я его не вставлю, и предлагает использовать -XScopedTypeVariables - person Dan Burton; 06.01.2012
comment
@Дэн Спасибо. По-видимому, эта прагма требуется в любых объявлениях типов при сопоставлении с образцом. Например в [1,2] >>= \(x::Int) -> [x+1]. - person is7s; 07.01.2012

Одним из отсталых решений является злоупотребление функцией «необязательный аргумент»:

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

Это тихо работает, даже если вы укажете () в качестве второго аргумента:

$ runhaskell args.hs 3 ()
3

И немного портит сообщение об использовании:

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

Но он отклоняет дополнительные аргументы, отличные от (), и работает так, как хотелось бы:

$ runhaskell args.hs 3
3
person Dan Burton    schedule 06.01.2012
comment
Не самое чистое решение, но я должен признать, что почти сразу же подумал о чем-то действительно похожем на него. - person Tikhon Jelvis; 06.01.2012