#haskell #types #type-signature
#haskell #типы #область видимости #несоответствие типов
Вопрос:
У меня проблема с определением области видимости Haskell в where
определениях. Когда у меня есть следующая функция f
, где я хочу передать x
локально определенной функции f1
без явного использования ее в качестве параметра, я получаю сообщение об ошибке, в котором говорится, что тип x
несовместим с типом в выводе f1
, хотя он должен быть таким же:
f :: Eq a => a -> [a] f x = f1 x , где f1 :: Eq a => a -> [a] f1 y = [x, y]
Ошибка заключается в следующем:
Не удалось сопоставить ожидаемый тип 'a1` с предполагаемым типом 'a' `a1' - это переменная жесткого типа, связанная сигнатурой типа для `f1' при тестировании.hs: 4:11 `a' - это переменная жесткого типа, связанная сигнатурой типа для `f' при тестировании.hs: 1: 8 В выражении: x В выражении: [x, y] В определении `f1': f1 y = [x, y] Сбой, загруженные модули: нет.
Однако, когда я передаю в x
качестве дополнительного параметра, как я сделал в следующем коде с функцией g
, она работает нормально:
g :: Eq a => a -> [a] g x = g1 x x , где g1 :: Eq a => a -> a -> [a] g1 x y = [x, y]
Есть ли способ сделать тип a
in f
совместимым с типом a
(или a1
) in f1
?
Комментарии:
1. Хотя очень хорошей практикой является добавление в ваш код множества сигнатур типов, за исключением чрезвычайно тривиальных частей, я не вижу причин добавлять к ним функции с локальной областью видимости. Очевидно, что такое сигнатура типа, и для подобных тривиальных функций вы теряете больше в удобочитаемости, чем получаете.
2. Ну, фактическая функция была более сложной, я просто сократил ее до простого примера, чтобы моя реальная проблема стала понятной.
Ответ №1:
Дэйв прямо выше. Другой способ подумать об этом заключается в том, что, хотя обе ваши подписи типов ссылаются на переменную a
, на самом деле это не переменная одного и того же типа. В нотации Haskell-prime обе подписи могут быть более явно записаны как:
forall a . Eq a => a -> [a]
это означает, что для обеих функций они могут принимать аргумент любого типа (в Eq). Очевидно, что здесь это не так. В стандартном 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 ]
и это будет работать нормально.
Ответ №2:
Проблема с вашим кодом заключается в сигнатуре типа f1 с локальной областью видимости. Он указывает, что f1 может принимать любой тип
f1 :: Eq a => a -> [a]
Несмотря на то, что это локальная функция, вы обобщили эту функцию, чтобы иметь возможность принимать тип, который не будет существовать в f , все, что получает эта функция, ДОЛЖНО поступать из f , поэтому сигнатура типа не нужна.
Просто удалите подпись типа f1.
Редактировать: прочитайте мой пост для себя, это немного неясно. a в f1 — это параметризованный тип, который может принимать что угодно, но переданные ему аргументы уже привязаны к f . Таким образом, эта функция может получать только то, что получает ее родительская функция, сигнатура типа, которую вы ей предоставляете, нарушает это правило. Надеюсь, это немного более понятно.
Комментарии:
1. Да, это работает, спасибо 🙂 Хотя это все еще вызывает опасения, что невозможно явно ввести
f1
функцию (я исхожу из гораздо более строгого языка), но хорошо, пока ничего не ломается 😉2. Poke: проблема здесь в том, что Haskell более строг к типам, чем вы ожидаете. Ваша исходная подпись типа для ‘f1’ указывала, что аргументом может быть что угодно, принадлежащее классу типов ‘Eq’, но компилятор правильно увидел, что аргумент, переданный в ‘f1’, на самом деле должен был быть именно тем, что было передано в ‘f’. Удаление подписи типа для ‘f1’ не ослабляет строгость типа. Это позволяет компилятору только выводить ввод за вас.