Сопоставление с образцом в выражении let - PullRequest
11 голосов
/ 20 июня 2010

Как извлечь значение из переменной неизвестного конструктора?

Например, я хотел бы отменить значение в Either, если было построено как Right:

let Right x = getValue
in Right (negate x)

Этот код успешно связывает значение Right (в данном случае Int) с x.

Это работает, но что если getValue возвращает Left?Есть ли способ определить тип переменной в выражении let?Или есть лучший способ решить эту проблему?

Ответы [ 2 ]

18 голосов
/ 20 июня 2010

В общем, что вы можете сделать, это:

case getValue of
  Right x -> Right $ negate x
  e       -> e

То, что это делает, должно быть ясно: это похоже на сопоставление с образцом в аргументе функции, но по значению. Для того, чтобы сделать то, что вам нужно, у вас есть регистр по умолчанию, который перехватывает все, что не соответствует, а затем возвращает это.

В вашем конкретном случае, однако, вы можете сделать что-то более приятное:

negate `fmap` getValue

Или, с import Control.Applicative, вы можете использовать <$> как синоним fmap (negate <$> getValue). Функция fmap имеет тип fmap :: Functor f => (a -> b) -> f a -> f b. Для любого функтора 1 , fmap преобразует функцию с обычными значениями в функцию внутри функтора. Например, списки являются функтором, а для списков fmap = map. Здесь Either e представляет функтор, который является либо исключением Left e, либо значением Right a; применение функции к Left ничего не делает, но применение функции к Right применяет ее в Right. Другими словами,

instance Functor (Either e) where
  fmap _ (Left l)  = Left l
  fmap f (Right r) = Right $ f r

Таким образом, версия case является прямым ответом на ваш вопрос, но ваш конкретный пример более точно аппроксимируется fmap.

1: В первом приближении функторы являются «контейнерами». Если вам не нравятся различные классы типов, я рекомендую Typeclassopedia для полной справки; Есть много других учебных пособий, и лучший способ почувствовать их - просто поиграть с ними. Однако fmap для определенных типов часто легко использовать (особенно, на мой взгляд, когда написано <$>).

2 голосов
/ 20 июня 2010

Ответ на заголовок этого вопроса:
Я не вижу большой разницы между "... where" и "let ... in ...". И то, и другое позволяет вам объявить несколько случаев привязки аргументов функции:

f val = let negR (Right x) = Right (negate x)
            negR y = y
        in negR val

или let { negR (Right x) = Right (negate x); negR y = y; } in negR val

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...