-
PartialTypeSignatures -
- С тех пор:
-
7.10.1
Проверяющий типов позволит выводить типы для дыр.
Частичная сигнатура типа — это сигнатура типа, содержащая специальные заполнительные символы, называемые подстановочными знаками. Подстановочный знак записывается как нижнее подчеркивание (например, «_») или, если включен NamedWildCards, любой идентификатор с ведущим нижним подчеркиванием (например, «_foo», «_bar»). Частичные сигнатуры типов относятся к сигнатурам типов так же, как Типизированные дыры относятся к выражениям. Во время компиляции эти подстановочные знаки или дыры будут генерировать сообщение об ошибке, которое описывает, какой тип был выведен в позиции дыры, и информацию об источнике любых свободных переменных типа. GHC по умолчанию сообщает такие сообщения об ошибках.
В отличие от Типизированных дыр, которые делают программу неполной и генерируют ошибки при их оценке, это необязательно для дыр в сигнатурах типов. Проверяющий типов способен (в большинстве случаев) проверить привязку с или без сигнатуры типа. Частичная сигнатура типа заполняет пробел между двумя крайностями, программист может выбрать, какие части типа аннотировать, а какие оставить для вывода проверяющим типов.
По умолчанию проверяющий типов будет сообщать сообщение об ошибке для каждой дыры в частичной сигнатуре типа, информируя программиста о выведенном типе. Когда включен PartialTypeSignatures расширение, проверяющий типов будет принимать выведенный тип для каждой дыры, генерируя предупреждения вместо ошибок. Кроме того, эти предупреждения можно отключить с помощью -Wno-partial-type-signatures флаг.
Однако, поскольку GHC должен вывести тип, когда часть типа пропущена, он не может использовать полиморфную рекурсию. То же ограничение применяется, когда сигнатура типа полностью пропущена.
Частичная сигнатура типа также заставляет GHC обобщать привязку, даже если MonoLocalBinds включен; см. Обобщение let.
6.11.7.1. Синтаксис
Полная (частичная) сигнатура типа имеет следующий вид: forall a b .. . (C1, C2, ..) => tau. Она состоит из трех частей:
- Переменные типа:
a b .. - Ограничения:
(C1, C2, ..) - Тип (монотип):
tau
Мы различаем три типа подстановочных знаков.
6.11.7.1.1. Подстановочные знаки типа
Подстановочные знаки, встречающиеся в части монотипа (тау) сигнатуры типа, являются подстановочными знаками типа («тип» часто опускается, так как это по умолчанию вид подстановочного знака). Подстановочные знаки типа могут быть привязаны к любому монотипу, такому как Bool или Maybe [Bool], включая функции и типы высшего уровня, такие как (Int -> Bool) или Maybe.
not' :: Bool -> _ not' x = not x -- Inferred: Bool -> Bool maybools :: _ maybools = Just [True] -- Inferred: Maybe [Bool] just1 :: _ Int just1 = Just 1 -- Inferred: Maybe Int filterInt :: _ -> _ -> [Int] filterInt = filter -- has type forall a. (a -> Bool) -> [a] -> [a] -- Inferred: (Int -> Bool) -> [Int] -> [Int]
Например, первый подстановочный знак в сигнатуре типа not' выведет следующее сообщение об ошибке:
Test.hs:4:17: error:
• Found type wildcard ‘_’ standing for ‘Bool’
To use the inferred type, enable PartialTypeSignatures
• In the type signature:
not' :: Bool -> _
• Relevant bindings include
not' :: Bool -> Bool (bound at Test.hs:5:1)
Когда подстановочный знак не привязан к монотипу, он будет обобщен, т.е. заменен новой переменной типа, например
foo :: _ -> _ foo x = x -- Inferred: forall t. t -> t filter' :: _ filter' = filter -- has type forall a. (a -> Bool) -> [a] -> [a] -- Inferred: (a -> Bool) -> [a] -> [a]
6.11.7.1.2. Именованные подстановочные знаки
-
NamedWildCards -
Разрешить именование подстановочных знаков (например,
_x). в сигнатурах типов.
Подстановочные знаки типа также могут быть именованы, присваивая подчеркиванию идентификатор в качестве суффикса, т. е. _a. Эти подстановочные знаки называются именованными подстановочными знаками. Все вхождения одного и того же именованного подстановочного знака в одной сигнатуре типа будут объединены в один тип. Например:
f :: _x -> _x
f ('c', y) = ('d', error "Urk")
-- Inferred: forall t. (Char, t) -> (Char, t)
Именованный подстановочный знак заставляет аргумент и типы результатов быть одинаковыми. Без сигнатуры GHC вывел бы forall a b. (Char, a) -> (Char, b). Именованный подстановочный знак может упоминаться в ограничениях, при условии, что он также встречается в части монотипа сигнатуры типа, чтобы убедиться, что он объединяется с чем-то:
somethingShowable :: Show _x => _x -> _ somethingShowable x = show x -- Inferred type: Show a => a -> String somethingShowable' :: Show _x => _x -> _ somethingShowable' x = show (not x) -- Inferred type: Bool -> String
Помимо подстановочного знака дополнительных ограничений (см. Дополнительный подстановочный знак ограничений), только именованные подстановочные знаки могут встречаться в ограничениях, например, _x в Show _x.
Если ScopedTypeVariables включен, именованные подстановочные знаки сигнатуры функции охватывают тело функции, как и явно объявленные переменные типа (Лексически-скопированные переменные типа), хотя явного forall нет. Например:
f :: _a -> _a
f x = let g :: _a -> _a
g = ...
in ...
Здесь именованный подстановочный знак _a охватывает тело f, тем самым связывая вхождения _a в сигнатуре g. Все четыре вхождения означают один и тот же тип.
Именованные подстановочные знаки не следует путать с переменными типа. Несмотря на синтаксическое сходство, именованные подстановочные знаки могут объединяться с монотипами, а также обобщаться (и вести себя как переменные типа).
В первом примере выше, _x обобщается (и фактически заменяется новой переменной типа a). Во втором примере, _x объединяется с типом Bool, и поскольку Bool реализует класс типов Show, ограничение Show Bool может быть упрощено.
По умолчанию GHC (как предписывает стандарт Haskell 2010) интерпретирует идентификаторы, начинающиеся с нижнего подчеркивания в типе, как переменные типа. Чтобы обработать их как именованные подстановочные знаки, следует включить NamedWildCards расширение. Пример ниже продемонстрировал эффект.
foo :: _a -> _a foo _ = False
Компиляция этой программы без включения NamedWildCards генерирует сообщение об ошибке, которое жалуется на переменную типа _a не соответствует фактическому типу Bool.
Test.hs:5:9: error:
• Couldn't match expected type ‘_a’ with actual type ‘Bool’
‘_a’ is a rigid type variable bound by
the type signature for:
foo :: forall _a. _a -> _a
at Test.hs:4:8
• In the expression: False
In an equation for ‘foo’: foo _ = False
• Relevant bindings include foo :: _a -> _a (bound at Test.hs:5:1)
Компиляция этой программы с включенными NamedWildCards (а также PartialTypeSignatures) расширением генерирует следующее сообщение об ошибке, которое сообщает о выведенном типе именованного подстановочного знака _a.
Test.hs:4:8: warning: [-Wpartial-type-signatures]
• Found type wildcard ‘_a’ standing for ‘Bool’
• In the type signature:
foo :: _a -> _a
• Relevant bindings include
foo :: Bool -> Bool (bound at Test.hs:5:1)
6.11.7.1.3. Подстановочный знак дополнительных ограничений
Третий тип подстановочного знака — подстановочный знак дополнительных ограничений. Наличие подстановочного знака дополнительных ограничений указывает на то, что во время проверки типов может быть выведено произвольное количество дополнительных ограничений, которые будут добавлены в сигнатуру типа. В примере ниже подстановочный знак дополнительных ограничений используется для вывода трех дополнительных ограничений.
arbitCs :: _ => a -> String
arbitCs x = show (succ x) ++ show (x == x)
-- Inferred:
-- forall a. (Enum a, Eq a, Show a) => a -> String
-- Error:
Test.hs:5:12: error:
Found constraint wildcard ‘_’ standing for ‘(Show a, Eq a, Enum a)’
To use the inferred type, enable PartialTypeSignatures
In the type signature:
arbitCs :: _ => a -> String
Подстановочный знак дополнительных ограничений не должен препятствовать программисту уже прописывать известные или желаемые аннотированные ограничения, например:
-- Also a correct partial type signature:
arbitCs' :: (Enum a, _) => a -> String
arbitCs' x = arbitCs x
-- Inferred:
-- forall a. (Enum a, Show a, Eq a) => a -> String
-- Error:
Test.hs:9:22: error:
Found constraint wildcard ‘_’ standing for ‘()’
To use the inferred type, enable PartialTypeSignatures
In the type signature:
arbitCs' :: (Enum a, _) => a -> String
Подстановочный знак дополнительных ограничений также может привести к нулю дополнительных ограничений, которые должны быть выведены, например:
noCs :: _ => String
noCs = "noCs"
-- Inferred: String
-- Error:
Test.hs:13:9: error:
Found constraint wildcard ‘_’ standing for ‘()’
To use the inferred type, enable PartialTypeSignatures
In the type signature:
noCs :: _ => String
Поскольку один подстановочный знак дополнительных ограничений достаточно для вывода любого количества ограничений, в сигнатуре типа допускается только один, и он должен стоять последним в списке ограничений.
Именованные подстановочные знаки дополнительных ограничений не поддерживаются.