Spec-Zone.ru › Haskell 9

6.9.8. Перегруженные метки

OverloadedLabels
Since:

8.0.1

Включить использование синтаксиса перегруженных меток #foo.

GHC поддерживает перегруженные метки, вид идентификаторов, чьё толкование может зависеть как от типа, так и от литерального текста. Когда включён расширение OverloadedLabels, перегруженную метку можно записать с префиксом решётка, например #foo. Тип этого выражения IsLabel "foo" a => a.

Класс IsLabel определяется как:

class IsLabel (x :: Symbol) a where
  fromLabel :: a

Это довольно похоже на класс IsString (см. Перегруженные строковые литералы), но с дополнительным параметром типа, который делает текст метки доступным в виде строкового типа уровня типа (см. Типы уровня типа). Обратите внимание, что fromLabel имел дополнительный аргумент Proxy# x в GHC 8.0, но он был удалён в GHC 8.2, так как вместо этого можно использовать применение типа (см. Видимое применение типа).

Нет предопределённых экземпляров этого класса. Он по умолчанию не включён в область видимости, но может быть добавлен в область видимости путём импорта GHC.OverloadedLabels. В отличие от IsString, для IsLabel нет специальных правил по умолчанию.

Во время проверки типов GHC заменит вхождение перегруженной метки, например #foo на fromLabel @"foo" . Это будет иметь некоторый тип alpha и потребует решения ограничения класса IsLabel "foo" alpha.

Целью использования IsLabel является поддержка перегруженных полей записей и, возможно, анонимных записей. Таким образом, в будущем могут быть заданы экземпляры для базовых типов данных (в частности, (->)).

Если включено RebindableSyntax, перегруженные метки будут развёрнуты с использованием функции fromLabel, которая находится в области видимости, а не всегда с использованием GHC.OverloadedLabels.fromLabel.

При написании перегруженной метки между знаком решётки и последующим идентификатором не должно быть пробела. Расширение MagicHash использует постфиксные знаки решётки; если OverloadedLabels и MagicHash оба включены, то x#y означает x# y, но если включено только OverloadedLabels, то это означает x #y. Расширение UnboxedTuples делает (# единым лексемой, поэтому при включенном UnboxedTuples необходимо вставить пробел между открывающей скобкой и перегруженной меткой. Для избежания путаницы настоятельно рекомендуется вставлять пробел перед знаком решётки при использовании OverloadedLabels.

При использовании OverloadedLabels (или других расширений, использующих знаки решётки) в файле .hsc (см. Написание Haskell интерфейсов к коду C: hsc2hs) знаки решётки должны быть удвоены (напишите ##foo вместо #foo) для предотвращения их интерпретации как hsc2hs директивы.

Вот расширение примера доступа к записи в Типы уровня типа, показывающее, как перегруженная метка может использоваться как селектор записи:

{-# LANGUAGE DataKinds, KindSignatures, MultiParamTypeClasses,
             FunctionalDependencies, FlexibleInstances,
             OverloadedLabels, ScopedTypeVariables #-}

import GHC.OverloadedLabels (IsLabel(..))
import GHC.TypeLits (Symbol)

data Label (l :: Symbol) = Get

class Has a l b | a l -> b where
  from :: a -> Label l -> b

data Point = Point Int Int deriving Show

instance Has Point "x" Int where from (Point x _) _ = x
instance Has Point "y" Int where from (Point _ y) _ = y

instance Has a l b => IsLabel l (a -> b) where
  fromLabel x = from x (Get :: Label l)

example = #x (Point 1 2)

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

Примеры вновь разрешённого синтаксиса:

  • Прописные буквы в начале: #Foo эквивалентно getLabel @”Foo”
  • Цифры: #3.14 эквивалентно getLabel @”3.14”
  • Произвольные строки: #”Hello, World!” эквивалентно getLabel @”Hello, World!”

Вот пример более либерального использования этого расширения, доступного с GHC 9.6:

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedLabels      #-}
{-# LANGUAGE MagicHash             #-}

import Data.Foldable (traverse_)
import Data.Proxy (Proxy(..))
import GHC.OverloadedLabels (IsLabel(..))
import GHC.TypeLits (KnownSymbol, symbolVal)
import GHC.Prim (Addr#)

instance KnownSymbol symbol => IsLabel symbol String where
  fromLabel = symbolVal (Proxy :: Proxy symbol)

(#) :: String -> Int -> String
(#) _ i = show i

f :: Addr# -> Int -> String
f _ i = show i

main :: IO ()
main = traverse_ putStrLn
  [ #a
  , #number17
  , #do
  , #type
  , #Foo
  , #3
  , #199.4
  , #17a23b
  , #f'a'
  , #'a'
  , #'
  , #''notTHSplice
  , #...
  , #привет
  , #こんにちは
  , #"3"
  , #":"
  , #"Foo"
  , #"The quick brown fox"
  , #"\""
  , (++) #hello#world
  , (++) #"hello"#"world"
  , #"hello"# 1 -- equivalent to `(fromLabel @"hello") # 1`
  , f "hello"#2 -- equivalent to `f ("hello"# :: Addr#) 2`
  ]

См. Предложение GHC #170 для получения более подробной информации.

© 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/overloaded_labels.html

Spec-Zone.ru

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