Spec-Zone.ru › Haskell 9

6.2.18. Типизированные дыры

Типизированные дыры — это функция 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

6.2.18.1. Совместимые вырезки для типизированных дыр

GHC иногда предлагает подходящие вырезки для типизированных дыр, что настраивается несколькими флагами.

-fno-show-valid-hole-fits
По умолчанию:

выкл

Этот флаг можно переключить, чтобы полностью отключить отображение подходящих вырезок.

-fmax-valid-hole-fits=⟨n⟩
По умолчанию:

6

Список подходящих вырезок ограничен отображением не более 6 вырезок на дыру. Количество отображаемых вырезок можно установить этим флагом. Отключение ограничения с помощью -fno-max-valid-hole-fits отобразит все найденные подходящие вырезки.

-fshow-type-of-hole-fits
По умолчанию:

вкл

По умолчанию вырезки показывают тип вырезки. Это можно отключить, используя обратное значение этого флага.

-fshow-type-app-of-hole-fits
По умолчанию:

вкл

По умолчанию вырезки показывают тип применения, необходимый для соответствия этой вырезки типу дыры, например, для дыры (_ :: Int -> [Int]), mempty является вырезкой с mempty @(Int -> [Int]). Это можно отключить, используя обратное значение этого флага.

-fshow-docs-of-hole-fits
По умолчанию:

выкл

Иногда название и тип подходящей вырезки недостаточно, чтобы понять, что она представляет. Этот флаг добавляет документацию к вырезке, если она доступна (и модуль, из которого происходит функция, был скомпилирован с флагом -haddock).

-fshow-type-app-vars-of-hole-fits
По умолчанию:

вкл

По умолчанию вырезки показывают тип применения, необходимый для соответствия этой вырезки типу дыры, например, для дыры (_ :: Int -> [Int]), mempty :: Monoid a => a является вырезкой с mempty @(Int -> [Int]). Этот флаг включает/отключает отображение a ~ (Int -> [Int]) вместо mempty @(Int -> [Int]) в разделе «где» сообщения о подходящей вырезке.

-fshow-provenance-of-hole-fits
По умолчанию:

вкл

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

-funclutter-valid-hole-fits
По умолчанию:

выкл

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

6.2.18.1.1. Вырезки уточнения

Когда флаг -frefinement-level-hole-fits=⟨n⟩ установлен на значение, n большее, чем 0, GHC предложит список подходящих вырезок уточнения, которые являются подходящими вырезками, требующими до n уровней дополнительного уточнения для завершения, где каждый уровень представляет дополнительную дыру в вырезке, которую нужно заполнить. Например, рассмотрим дыру в

f :: [Integer] -> Integer
f = _

Когда уровень уточнения не задан, он будет предлагать только подходящие вырезки:

Valid hole fits include
  f :: [Integer] -> Integer
  head :: forall a. [a] -> a
    with head @Integer
  last :: forall a. [a] -> a
    with last @Integer
  maximum :: forall (t :: * -> *).
              Foldable t =>
              forall a. Ord a => t a -> a
    with maximum @[] @Integer
  minimum :: forall (t :: * -> *).
              Foldable t =>
              forall a. Ord a => t a -> a
    with minimum @[] @Integer
  product :: forall (t :: * -> *).
              Foldable t =>
              forall a. Num a => t a -> a
    with product @[] @Integer
  sum :: forall (t :: * -> *).
          Foldable t =>
          forall a. Num a => t a -> a
    with sum @[] @Integer

Однако, при установке -frefinement-level-hole-fits=⟨n⟩, например, на 1, он дополнительно предложит список вырезок уточнения, в этом случае:

Valid refinement hole fits include
  foldl1 (_ :: Integer -> Integer -> Integer)
    with foldl1 @[] @Integer
    where foldl1 :: forall (t :: * -> *).
                    Foldable t =>
                    forall a. (a -> a -> a) -> t a -> a
  foldr1 (_ :: Integer -> Integer -> Integer)
    with foldr1 @[] @Integer
    where foldr1 :: forall (t :: * -> *).
                    Foldable t =>
                    forall a. (a -> a -> a) -> t a -> a
  const (_ :: Integer)
    with const @Integer @[Integer]
    where const :: forall a b. a -> b -> a
  ($) (_ :: [Integer] -> Integer)
    with ($) @GHC.Types.LiftedRep @[Integer] @Integer
    where ($) :: forall a b. (a -> b) -> a -> b
  fail (_ :: String)
    with fail @((->) [Integer]) @Integer
    where fail :: forall (m :: * -> *).
                  Monad m =>
                  forall a. String -> m a
  return (_ :: Integer)
    with return @((->) [Integer]) @Integer
    where return :: forall (m :: * -> *). Monad m => forall a. a -> m a
  (Some refinement hole fits suppressed;
    use -fmax-refinement-hole-fits=N or -fno-max-refinement-hole-fits)

Что показывает, что дыру можно заменить, например, на foldl1 _. Хотя это не исправит дыру, это может помочь пользователям понять, какие у них есть варианты.

-frefinement-level-hole-fits=⟨n⟩
По умолчанию:

выкл

Список подходящих вырезок уточнения генерируется путем рассмотрения вырезок с различным количеством дополнительных дыр. Количество дыр в уточнении можно установить этим флагом. Если флаг установлен в 0 или вообще не установлен, подходящие вырезки уточнения не будут предложены.

-fabstract-refinement-hole-fits
По умолчанию:

выкл

Список подходящих вырезок уточнения может часто увеличиваться, когда уровень уточнения >= 2, с дырами, такими как head _ _ или fst _ _, которые являются допустимыми уточнениями, но которые маловероятно будут уместными, так как одна или несколько дыр все еще полностью открыты, поскольку ни тип, ни вид этих дыр не ограничены предлагаемым идентификатором вообще. По умолчанию такие дыры не сообщаются. При включении этого флага такие дыры включаются в список подходящих вырезок уточнения.

-fmax-refinement-hole-fits=⟨n⟩
По умолчанию:

6

Список подходящих вырезок уточнения ограничен отображением не более 6 вырезок на дыру. Количество отображаемых вырезок можно установить этим флагом. Отключение ограничения с помощью -fno-max-refinement-hole-fits отобразит все найденные подходящие вырезки.

-fshow-hole-matches-of-hole-fits
По умолчанию:

вкл

Типы дополнительных дыр в вырезках уточнения отображаются в выводе, например, foldl1 (_ :: a -> a -> a) является уточнением для дыры _ :: [a] -> a. Если этот флаг выключен, вывод отобразит только foldl1 _, что можно использовать как прямую замену дыры, без необходимости -XScopedTypeVariables.

6.2.18.1.2. Сортировка подходящих вырезок

В настоящее время есть два способа сортировки подходящих вырезок. Сортировка может быть включена/выключена с помощью -fsort-valid-hole-fits

-fno-sort-valid-hole-fits
По умолчанию:

выкл

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

-fsort-by-size-hole-fits
По умолчанию:

вкл

Сортировка по размеру типов, которые должны иметь квантифицированные переменные типа в типе функции, чтобы соответствовать типу дыры.

-fsort-by-subsumption-hole-fits
По умолчанию:

выкл

Альтернативная сортировка. Сортировка по проверке, какие вырезки подразумевают другие вырезки, так что если вырезка a может использоваться как вырезка для вырезки b, то b появляется перед a в выводе. Она более точна, чем стандартная сортировка, но также намного медленнее, поскольку для каждой пары подходящих вырезок должна выполняться проверка субсуммирования.

© 2002–2007 The University Court of the University of Glasgow. All rights reserved.
Licensed under the Glasgow Haskell Compiler License.
https://downloads.haskell.org/~ghc/9.12.1/docs/users_guide/exts/typed_holes.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API