Spec-Zone.ru › Haskell 9

6.4.14. Полиморфизм представлений

Для обеспечения полной гибкости в использовании типов необходимо использовать систему типов для различения упакованных, поднятых типов (обычных типов, таких как Int и [Bool]) и распакованных, примитивных типов (Распакованные типы и примитивные операции) , таких как Int#. Таким образом, у нас есть так называемый полиморфизм представлений.

Вот ключевые определения, все доступные из GHC.Exts:

TYPE :: RuntimeRep -> Type   -- highly magical, built into GHC

data Levity = Lifted    -- for things like `Int`
            | Unlifted  -- for things like `Array#`

data RuntimeRep = BoxedRep Levity  -- for anything represented by a GC-managed pointer
                | IntRep           -- for `Int#`
                | TupleRep [RuntimeRep]  -- unboxed tuples, indexed by the representations of the elements
                | SumRep [RuntimeRep]    -- unboxed sums, indexed by the representations of the disjuncts
                | ...

type LiftedRep = BoxedRep Lifted

type Type = TYPE LiftedRep    -- Type is just an ordinary type synonym

Идея состоит в том, что у нас есть новая фундаментальная константа типа TYPE, которая параметризована RuntimeRep. Таким образом, мы получаем Int# :: TYPE IntRep и Bool :: TYPE LiftedRep. Всё с типом в форме TYPE x может появляться по обе стороны от стрелки функции ->. Таким образом, мы можем сказать, что -> имеет тип TYPE r1 -> TYPE r2 -> TYPE LiftedRep. Результат всегда поднят, потому что все функции подняты в GHC.

6.4.14.1. Полиморфизм лёгкости

Особым случаем полиморфизма представлений является полиморфизм лёгкости, где мы абстрагируемся от переменной типа Levity, например:

example :: forall (l :: Levity) (a :: TYPE (BoxedRep l)). (Int -> a) -> a
example f = f 42

С помощью UnliftedDatatypes, мы можем даже объявлять полиморфные по лёгкости типы данных:

type PEither :: Type -> Type -> TYPE (BoxedRep l)
data PEither l r = PLeft l | PRight r

6.4.14.2. Переменные и аргументы, не являющиеся полиморфными по представлению

Если бы GHC не должен был компилировать программы, выполняемые в реальном мире, это было бы всё. Но полиморфизм представлений может создать немало проблем для генератора кода GHC. Рассмотрим

bad :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)
              (a :: TYPE r1) (b :: TYPE r2).
       (a -> b) -> a -> b
bad f x = f x

Это похоже на обобщение стандартного оператора $. Однако, если мы подумаем о компиляции этого в выполняемый код, появляются проблемы. В частности, когда мы вызываем bad, мы должны каким-то образом передать x в bad. Какой ширины (то есть, сколько бит) является x? Это указатель? Какой тип регистра (с плавающей запятой или целочисленный) должен быть у x? Всё это невозможно сказать, потому что тип x , a :: TYPE r1 является полиморфным по представлению. Таким образом, мы запрещаем такие конструкции с помощью следующего простого правила:

Переменная не может иметь тип, полиморфный по представлению.

Это устраняет bad , потому что переменная x имела бы тип, полиморфный по представлению.

Однако, всё не потеряно. Мы всё ещё можем сделать это:

good :: forall r (a :: Type) (b :: TYPE r).
       (a -> b) -> a -> b
good f x = f x

Здесь только b является полиморфным по представлению. Нет переменных с типом, полиморфным по представлению. И генератор кода не имеет проблем с этим. Тем не менее, есть способ написать определение с типом bad:

($) :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)
              (a :: TYPE r1) (b :: TYPE r2).
       (a -> b) -> a -> b
($) f = f

С помощью эта-редукции мы избавились от x и, следовательно, у нас нет переменной с типом, полиморфным по представлению. Фактически, это истинный тип оператора GHC $ , немного более общий, чем версия Haskell 98. Однако, его свойства строгости отличаются: (good undefined) `seq` () эквивалентно (), в то время как (($) undefined) `seq` () расходится.

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

6.4.14.3. Полиморфные по представлению «дно»

Мы можем использовать полиморфизм представлений с хорошим эффектом с error и undefined , типы которых приведены здесь:

undefined :: forall (r :: RuntimeRep) (a :: TYPE r).
             HasCallStack => a
error :: forall (r :: RuntimeRep) (a :: TYPE r).
         HasCallStack => String -> a

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

6.4.14.4. Выведение и по умолчанию

GHC не выводит типы, полиморфные по представлению. Если представление переменной не указано, оно будет предполагаться как LiftedRep. Например, если вы напишете f a b = a b, выведенный тип f будет

f :: forall {a :: Type} {b :: Type}. (a -> b) -> a -> b

хотя

f :: forall {rep} {a :: Type} {b :: TYPE rep}. (a -> b) -> a -> b

также было бы допустимо, как описано выше.

Аналогично, в пользовательском сигнатуре f :: forall a b. (a -> b) -> a -> b GHC будет предполагать, что как a , так и b имеют тип Type. Чтобы использовать другое представление, вы должны указать типы a и b.

Во время вывода типов GHC не квантифицирует по переменным типа RuntimeRep ни Levity. Вместо этого они по умолчанию принимают значения LiftedRep и Lifted соответственно. Аналогично, переменные Multiplicity (Линейные типы) по умолчанию принимают значение Many.

6.4.14.5. Вывод типов, полиморфных по представлению

-fprint-explicit-runtime-reps

Печать параметров RuntimeRep и Levity по мере их появления; в противном случае они по умолчанию принимают значения LiftedRep и Lifted соответственно.

Большинству пользователей GHC не нужно беспокоиться о полиморфизме представлений или распакованных типах. Для этих пользователей отображение полиморфизма представления в типе $ бесполезно. И поэтому по умолчанию он подавляется, предполагая, что все переменные типа RuntimeRep равны LiftedRep при печати, а печать TYPE LiftedRep как Type (или * когда StarIsType включено).

Если вам нужно увидеть полиморфизм представления в ваших типах, включите флаг -fprint-explicit-runtime-reps. Например,

ghci> :t ($)
($) :: (a -> b) -> a -> b
ghci> :set -fprint-explicit-runtime-reps
ghci> :t ($)
($)
  :: forall (r :: GHC.Types.RuntimeRep) a (b :: TYPE r).
     (a -> b) -> a -> b

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

Spec-Zone.ru

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