Избегайте неуместного неисчерпывающего предупреждения о совпадении с образцом в GHCI - PullRequest
0 голосов
/ 16 февраля 2019

Прежде чем вы отклоните это как дубликат

, я вижу, что по крайней мере с сентября 2018 года GHCI не позволяет вам отключить предупреждение локально (хотя вы можете сделать это в целом файле).

Но, может быть, есть какой-то другой способ сообщить GHCI, что каждый случай фактически обрабатывается?

Вопрос

Иногда я использую идиому, чтобы написать функцию, в которойпервое определение (я) проверяет некоторый предикат (ы) и возвращает Left, а другие определения рассматривают аргументы, в которых операция имеет смысл.Всякий раз, когда я это делаю, я получаю ошибку «Сопоставление с образцом (ями) не являются исчерпывающими», но я действительно проверяю каждое условие.

Пример

(Для реального кодамотивируя этот пример игрушки, см., например, определение pExprToHExpr здесь .)

Этот код:

{-# LANGUAGE ViewPatterns #-}

data Cowbell = Cowbell
  deriving Show
data Instrument = Rattle
                | Drums (Maybe Cowbell)
                | Violin
                | Oboe
  deriving Show

pitched :: Instrument -> Bool
pitched Rattle                 = False
pitched (Drums Nothing)        = False
pitched (Drums (Just Cowbell)) = True
pitched Violin                 = True
pitched Oboe                   = True

highestPitch :: Instrument -> Either String Float
highestPitch i@(pitched -> False) =
  Left $ "Instrument " ++ show i ++ " has no pitch."
highestPitch (Drums (Just Cowbell)) = Right 800
highestPitch Violin                 = Right 5000
highestPitch Oboe                   = Right 2000

генерирует эту ошибку:

example.hs:19:1: warning: [-Wincomplete-patterns]
    Pattern match(es) are non-exhaustive
    In an equation for ‘highestPitch’:
        Patterns not matched:
            Rattle
            (Drums Nothing)

При других условиях я бы просто поделил тип Instrument:

data Percussive = Rattle | Drums
data Pitched    = Violin | Oboe
data Instrument = Percussive Percussive
                | Pitched Pitched

Но (в этой воображаемой физике) набор Drums может иметь наивысшую высоту, если он включает Cowbell, поэтому он не подходит ни к типу Percussive, ни к Pitched.

Ответы [ 2 ]

0 голосов
/ 16 февраля 2019

Я бы поменял, какая функция является «окончательной».Даже если инструмент имеет тональность, вам все равно нужно назначить наивысшую высоту тона, в то время как highestPitch предоставляет все, что вам нужно знать, тональность инструмента или нет.Таким образом

-- An instrument with a highest pitch is pitched; the others aren't.
pitched :: Instrument -> Bool
pitched = either (const False) (const True) . highestPitch


highestPitch :: Instrument -> Either String Float
highestPitch (Drums (Just Cowbell)) = Right 800
highestPitch Violin                 = Right 5000
highestPitch Oboe                   = Right 2000
highestPitch Rattle = Left "Instrument Rattle has no pitch"
highestPitch (Drums Nothing) = Left "Instrument Drums Nothing has no pitch"
0 голосов
/ 16 февраля 2019

GHC не проверяет полноту с учетом определения pitched.Следовательно, первое уравнение по существу игнорируется средством проверки, в результате чего возникает предупреждение.

Действительно, с точки зрения вычислительной техники, GHC не может решить это в общем случае, так как исчерпывающий при наличии зренияшаблоны неразрешимы.В лучшем случае GHC может использовать некоторый сложный статический анализ, но вместо этого он просто предпочитает полностью игнорировать pitched.

Чтобы отключить предупреждение, я вижу два основных варианта.Во-первых, в конце необходимо добавить регистр «поймать все».

highestPitch :: Instrument -> Either String Float
highestPitch i@(pitched -> False) =
  Left $ "Instrument " ++ show i ++ " has no pitch."
highestPitch (Drums (Just Cowbell)) = Right 800
highestPitch Violin                 = Right 5000
highestPitch Oboe                   = Right 2000
highestPitch i                      =
  Left $ "Instrument " ++ show i ++ " has no pitch."
  -- we could even use "error", in certain cases

Если мы пойдем по этому маршруту, в данном конкретном случае мы можем удалить первое уравнение.

highestPitch :: Instrument -> Either String Float
highestPitch (Drums (Just Cowbell)) = Right 800
highestPitch Violin                 = Right 5000
highestPitch Oboe                   = Right 2000
highestPitch i                      =
  Left $ "Instrument " ++ show i ++ " has no pitch."

В качестве альтернативы,мы могли бы сделать последний случай Oboe всеобъемлющим:

highestPitch :: Instrument -> Either String Float
highestPitch i@(pitched -> False) =
  Left $ "Instrument " ++ show i ++ " has no pitch."
highestPitch (Drums (Just Cowbell)) = Right 800
highestPitch Violin                 = Right 5000
highestPitch _oboe                  = Right 2000   -- must be an Oboe

Я не большой поклонник этого подхода, хотя, так как, если pitched содержит ошибку, это будет тихо производить тонакоторого не должно было быть.

На самом деле, как указано выше в комментариях, существует третий способ: использование PatternSynonyms и COMPLETE прагмы, чтобы убедить GHC замолчать предупреждение.Это, однако, более продвинутый.Хотя он определенно находит применение в разработке библиотек, для этого конкретного случая это, вероятно, немного излишне.

...