Область видимости Haskell в определениях вложенных функций, где - PullRequest
19 голосов
/ 30 ноября 2009

У меня проблема с областью действия Хаскелла в определениях where. Когда у меня есть следующая функция f, где я хочу передать x локально определенной функции f1 без явного использования ее в качестве параметра, я получаю сообщение о том, что тип x несовместим с тот, что выводится f1, хотя он должен быть таким же:

f :: Eq a => a -> [a]
f x = f1 x
    where
        f1 :: Eq a => a -> [a]
        f1 y = [ x, y ]

Ошибка следующая:

    Couldn't match expected type `a1' against inferred type `a'
      `a1' is a rigid type variable bound by
           the type signature for `f1' at test.hs:4:11
      `a' is a rigid type variable bound by
          the type signature for `f' at test.hs:1:8
    In the expression: x
    In the expression: [x, y]
    In the definition of `f1': f1 y = [x, y]
Failed, modules loaded: none.

Однако, когда я передаю x в качестве дополнительного параметра, как я делал в следующем коде с функцией g, он прекрасно работает:

g :: Eq a => a -> [a]
g x = g1 x x
    where
        g1 :: Eq a => a -> a -> [a]
        g1 x y = [ x, y ]

Есть ли способ сделать тип a в f совместимым с типом a (или a1) в f1?

Ответы [ 2 ]

18 голосов
/ 30 ноября 2009

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

forall a . Eq a => a -> [a]

означает, что для обеих функций они могут принимать аргументы любого типа (в уравнении). Это явно не тот случай. В стандартном Haskell 98 единственным вариантом является отказ от подписи типа для f1. Но GHC (и другие?) Поддерживают переменные типа с лексической областью . Чтобы вы могли написать

{-# LANGUAGE ScopedTypeVariables #-}

f :: forall a. Eq a => a -> [a]
f x = f1 x
    where
        f1 :: a -> [a]
        f1 y = [ x, y ]

и это будет нормально работать.

11 голосов
/ 30 ноября 2009

Проблема с вашим кодом - локальная подпись типа f1. Указывает, что f1 может принимать любой тип

f1 :: Eq a => a -> [a]

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

Просто удалите подпись типа f1.

Редактировать: Прочтите мой пост самому себе, это немного неясно. a в f1 является параметризованным типом, который может принимать что угодно, но передаваемые ему аргументы уже связаны в f. Таким образом, эта функция может получать только то, что получает ее родительская функция, подпись типа, которую вы ей даете, нарушает это правило. Надеюсь, это немного яснее.

...