Spec-Zone.ru › Haskell 9

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

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

Иногда GHC предлагает допустимые варианты заполнения типизированных дыр; это поведение настраивается несколькими флагами.

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

off

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

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

6

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

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

on

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

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

on

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

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

off

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

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

on

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

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

on

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

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

off

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

6.2.19.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⟩
По умолчанию:

off

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

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

off

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

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

6

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

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

on

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

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

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

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

off

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

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

on

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

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

off

Альтернативный способ сортировки. Сортировка выполняется путём проверки того, какие варианты заполнения дыр поглощают другие варианты: если вариант заполнения 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.14.1/docs/users_guide/exts/typed_holes.html

Spec-Zone.ru

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