В чем разница между "err @ (Left _)" и "Left err" - PullRequest
3 голосов
/ 15 ноября 2010

Я пробую коды из .

В версии 6.10.4 GHC:

data ParseState = ParseState {
  string :: String
} deriving (Show)

newtype Parse a = Parse {
  runParse :: ParseState -> Either String (a, ParseState)
}

parse :: Parse a -> String -> Either String a
parse parser initState =
  case runParse parser (ParseState initState) of
    Left err          -> Left err
    Right (result, _) -> Right result

Все шло нормально, пока я не изменил 'Left err' на 'err @ (Left _)':

--  err@(Left _)      -> err
{-
 -  Occurs check: cannot construct the infinite type:
 -    a = (a, ParseState)
 -  When generalising the type(s) for `parse'
-}

Есть идеи?

1 Ответ

8 голосов
/ 15 ноября 2010

Это тонко.case проверяет значение типа Either String (a, ParseState), поэтому при присвоении имени шаблону в

err@(Left _) -> err

err этот же тип имеет.Однако возвращаемый тип функции говорит, что это должен быть Either String a, который не соответствует типу err Either String (a, ParseState).Глядя на тип Left:

Left :: x -> Either x y

Когда вы используете Left в правой части в

Left err -> Left err

Вы даете ему возможность выбрать другойy, а именно a вместо (a, ParseState).

Так что, хотя значения одинаковы, типы не совпадают, и поэтому они могутНе подставляйте.

Кстати, в вашем случае в Control.Arrow есть несколько очень удобных функций (специализирующихся на (->) для простоты):

left :: (a -> a') -> Either a b -> Either a' b
right :: (b -> b') -> Either a b -> Either a b'
(+++) :: (a -> a') -> (b -> b') -> Either a b -> Either a' b'

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

parse parser = right fst . runParse parser . ParseState
...