Типизированные дыры — это функция GHC, которая позволяет использовать специальные заполнители, записанные с ведущим символом подчёркивания (например, «_», «_foo», «_bar»), в качестве выражений. Во время компиляции эти дыры будут генерировать сообщение об ошибке, которое описывает ожидаемый тип в позиции дыры, информацию об источнике любых свободных переменных типа, и список локальных связей, которые могут помочь заполнить дыру, а также связи в области видимости, подходящие для типа дыры, которые могут помочь заполнить дыру фактическим кодом. Типизированные дыры всегда включены в GHC.
Цель типизированных дыр — помочь в написании кода Haskell, а не изменить систему типов. Типизированные дыры могут использоваться для получения дополнительной информации от проверяющего типов, которая иначе может быть трудно получить. Обычно, используя GHCi, пользователи могут просмотреть (выведенные) сигнатуры типов всех связей верхнего уровня. Однако этот метод менее удобен для терминов, которые не определены на верхнем уровне или внутри сложных выражений. Дыры позволяют пользователю проверить тип термина, который они собираются написать.
Например, компиляция следующего модуля с помощью GHC:
f :: a -> a f x = _
приведёт к ошибке:
hole.hs:2:7:
Found hole `_' with type: a
Where: `a' is a rigid type variable bound by
the type signature for f :: a -> a at hole.hs:1:6
In the expression: _
In an equation for `f': f x = _
Relevant bindings include
x :: a (bound at hole.hs:2:3)
f :: a -> a (bound at hole.hs:2:1)
Valid hole fits include x :: a (bound at hole.hs:2:3)
Вот некоторые дополнительные сведения:
-
Ошибка «
Found hole» обычно завершает компиляцию, как и любая другая ошибка типа. В конце концов, вы опустили часть кода из вашей программы. Тем не менее, вы можете запустить и протестировать фрагмент кода, содержащий дыры, используя флаг-fdefer-typed-holes. Этот флаг откладывает ошибки, созданные типизированными дырами, до времени выполнения и преобразует их в предупреждения во время компиляции. Эти предупреждения, в свою очередь, могут быть полностью подавлены с помощью-Wno-typed-holes.То же поведение для ошибок «
Variable out of scope» — оно завершает компиляцию по умолчанию. Вы можете отложить такие ошибки, используя флаг-fdefer-out-of-scope-variables. Этот флаг откладывает ошибки, созданные переменными, выходящими за пределы области видимости, до времени выполнения и преобразует их в предупреждения во время компиляции. Эти предупреждения, в свою очередь, могут быть полностью подавлены с помощью-Wno-deferred-out-of-scope-variables.В результате дыра или переменная будет вести себя как
undefined, но с дополнительными преимуществами: она покажет предупреждение во время компиляции и отобразит то же сообщение, если она будет вычислена во время выполнения. Это поведение соответствует параметру-fdefer-type-errors, который подразумевает-fdefer-typed-holesи-fdefer-out-of-scope-variables. См. Откладывание ошибок типов до времени выполнения. -
Все несвязанные идентификаторы обрабатываются как типизированные дыры, независимо от того, начинаются ли они с подчёркивания или нет. Единственное различие заключается в сообщении об ошибке:
cons z = z : True : _x : y
вызывает ошибки
Foo.hs:3:21: error: Found hole: _x :: Bool Or perhaps ‘_x’ is mis-spelled, or not in scope In the first argument of ‘(:)’, namely ‘_x’ In the second argument of ‘(:)’, namely ‘_x : y’ In the second argument of ‘(:)’, namely ‘True : _x : y’ Relevant bindings include z :: Bool (bound at Foo.hs:3:6) cons :: Bool -> [Bool] (bound at Foo.hs:3:1) Valid hole fits include z :: Bool (bound at mpt.hs:2:6) otherwise :: Bool (imported from ‘Prelude’ at mpt.hs:1:8-10 (and originally defined in ‘GHC.Base’)) False :: Bool (imported from ‘Prelude’ at mpt.hs:1:8-10 (and originally defined in ‘GHC.Types’)) True :: Bool (imported from ‘Prelude’ at mpt.hs:1:8-10 (and originally defined in ‘GHC.Types’)) maxBound :: forall a. Bounded a => a with maxBound @Bool (imported from ‘Prelude’ at mpt.hs:1:8-10 (and originally defined in ‘GHC.Enum’)) minBound :: forall a. Bounded a => a with minBound @Bool (imported from ‘Prelude’ at mpt.hs:1:8-10 (and originally defined in ‘GHC.Enum’)) Foo.hs:3:26: error: Variable not in scope: y :: [Bool]Для явных дыр (т.е. тех, которые начинаются с подчёркивания) предоставляется больше информации, чем для переменных, выходящих за пределы области видимости, потому что последние часто являются непреднамеренными опечатками, поэтому дополнительная информация отвлекает. Если вам нужна подробная информация, используйте ведущее подчёркивание, чтобы явно указать своё намерение использовать дыру.
-
Несвязанные идентификаторы с одинаковым именем никогда не объединяются, даже в рамках одной функции, но отображаются по отдельности. Например:
cons = _x : _x
приводит к следующим ошибкам:
unbound.hs:1:8: Found hole '_x' with type: a Where: `a' is a rigid type variable bound by the inferred type of cons :: [a] at unbound.hs:1:1 In the first argument of `(:)', namely `_x' In the expression: _x : _x In an equation for `cons': cons = _x : _x Relevant bindings include cons :: [a] (bound at unbound.hs:1:1) unbound.hs:1:13: Found hole: _x :: [a] Where: ‘a’ is a rigid type variable bound by the inferred type of cons :: [a] at unbound.hs:3:1-12 Or perhaps ‘_x’ is mis-spelled, or not in scope In the second argument of ‘(:)’, namely ‘_x’ In the expression: _x : _x In an equation for ‘cons’: cons = _x : _x Relevant bindings include cons :: [a] (bound at unbound.hs:3:1) Valid hole fits include cons :: forall a. [a] with cons @a (defined at mpt.hs:3:1) mempty :: forall a. Monoid a => a with mempty @[a] (imported from ‘Prelude’ at mpt.hs:1:8-10 (and originally defined in ‘GHC.Base’))Обратите внимание на два разных типа, сообщаемых для двух разных случаев
_x. -
Для использования типизированных дыр не требуется расширение языка. Лексема «
_» ранее была недопустима в Haskell, но теперь имеет более информативное сообщение об ошибке. Лексема «_x» — это совершенно законная переменная, и её поведение не меняется, когда она находится в области видимости. Напримерf _x = _x + 1
не вызывает никаких ошибок. Только переменная, которая не находится в области видимости (независимо от того, начинается ли она с подчёркивания или нет), обрабатывается как ошибка (как всегда), хотя теперь с более информативным сообщением об ошибке.
- Несвязанные конструкторы данных, используемые в выражениях, ведут себя точно так же, как и выше. Однако несвязанные конструкторы данных, используемые в шаблонах, не могут быть отложены и вместо этого приводят к остановке компиляции. (В плане реализации они сообщаются ренамером, а не проверяющим типов.)
-
Список допустимых соответствий для дыры определяется путём проверки, какие связи в области видимости подходят для дыры. Например, компиляция следующего модуля с помощью GHC:
import Data.List (inits) g :: [String] g = _ "hello, world"
вызывает следующие ошибки:
• Found hole: _ :: [Char] -> [String] • In the expression: _ In the expression: _ "hello, world" In an equation for ‘g’: g = _ "hello, world" • Relevant bindings include g :: [String] (bound at mpt.hs:6:1) Valid hole fits include lines :: String -> [String] (imported from ‘Prelude’ at mpt.hs:3:8-9 (and originally defined in ‘base-4.11.0.0:Data.OldList’)) words :: String -> [String] (imported from ‘Prelude’ at mpt.hs:3:8-9 (and originally defined in ‘base-4.11.0.0:Data.OldList’)) inits :: forall a. [a] -> [[a]] with inits @Char (imported from ‘Data.List’ at mpt.hs:4:19-23 (and originally defined in ‘base-4.11.0.0:Data.OldList’)) repeat :: forall a. a -> [a] with repeat @String (imported from ‘Prelude’ at mpt.hs:3:8-9 (and originally defined in ‘GHC.List’)) fail :: forall (m :: * -> *). Monad m => forall a. String -> m a with fail @[] @String (imported from ‘Prelude’ at mpt.hs:3:8-9 (and originally defined in ‘GHC.Base’)) return :: forall (m :: * -> *). Monad m => forall a. a -> m a with return @[] @String (imported from ‘Prelude’ at mpt.hs:3:8-9 (and originally defined in ‘GHC.Base’)) pure :: forall (f :: * -> *). Applicative f => forall a. a -> f a with pure @[] @String (imported from ‘Prelude’ at mpt.hs:3:8-9 (and originally defined in ‘GHC.Base’)) read :: forall a. Read a => String -> a with read @[String] (imported from ‘Prelude’ at mpt.hs:3:8-9 (and originally defined in ‘Text.Read’)) mempty :: forall a. Monoid a => a with mempty @([Char] -> [String]) (imported from ‘Prelude’ at mpt.hs:3:8-9 (and originally defined in ‘GHC.Base’))
Существуют несколько флагов для управления объёмом информации о контексте, отображаемой для типизированных дыр:
-
-fshow-hole-constraints -
При сообщении о типизированных дырах также выводите ограничения, которые находятся в области видимости. Например:
f :: Eq a => a -> Bool f x = _
приводит к следующему сообщению:
show_constraints.hs:4:7: error: • Found hole: _ :: Bool • In the expression: _ In an equation for ‘f’: f x = _ • Relevant bindings include x :: a (bound at show_constraints.hs:4:3) f :: a -> Bool (bound at show_constraints.hs:4:1) Constraints include Eq a (from show_constraints.hs:3:1-22) Valid hole fits include otherwise :: Bool False :: Bool True :: Bool maxBound :: forall a. Bounded a => a with maxBound @Bool minBound :: forall a. Bounded a => a with minBound @Bool