Выражение бесконечных видов

При выражении бесконечных типов в Haskell:

f x = x x -- This doesn't type check

Для этого можно использовать newtype:

newtype Inf = Inf { runInf :: Inf -> * }

f x = x (Inf x)

Существует ли newtype эквивалент для видов, который позволяет выражать бесконечное количество видов?

Я уже обнаружил, что могу использовать семейства типов для получения чего-то подобного:

{-# LANGUAGE TypeFamilies #-}
data Inf (x :: * -> *) = Inf
type family RunInf x :: * -> *
type instance RunInf (Inf x) = x

Но меня это решение не устраивает - в отличие от эквивалентных типов, Inf не создает новый вид (Inf x имеет тип *), так что безопасность вида меньше.

Есть ли более элегантное решение этой проблемы?


person yairchu    schedule 16.12.2018    source источник
comment
Допустимые типы конечны. Хотя они могут быть самореференциальными. Чтобы создать самореферентный вид, вам нужен способ сослаться на вид, предположительно по имени. Я подозреваю, что DataKinds будут здесь полезны.   -  person n. 1.8e9-where's-my-share m.    schedule 18.12.2018
comment
@n.m.: Да, теперь это принятый ответ   -  person yairchu    schedule 19.12.2018


Ответы (3)


Отвечая на:

Подобно схемам рекурсии, мне нужен способ построения AST, за исключением того, что я хочу, чтобы мои AST могли ссылаться друг на друга, то есть терм может содержать тип (для лямбда-параметра), тип может содержать тип строки в нем и наоборот. Я бы хотел, чтобы AST были определены с помощью внешней фиксированной точки (чтобы можно было иметь «чистые» выражения или выражения, аннотированные типами после вывода типа), но я также хочу, чтобы эти фиксированные точки могли содержать другие типы фиксированные точки (точно так же, как термы могут содержать термы разных типов). Я не вижу, как Fix мне помогает в этом

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

{-# LANGUAGE DataKinds #-}

import Data.Kind (Type)

data Label = Label ((Label -> Type) -> Type)
type L = 'Label

L — это просто ярлык для создания меток.

Определения с открытой фиксированной точкой относятся к виду (Label -> Type) -> Type, то есть они принимают «функцию интерпретации (типа) метки» и возвращают тип. Я назвал их «функторами формы» и абстрактно назвал их буквой h. Простейший функтор формы — это тот, который не рекурсивно:

newtype LiteralF a f = Literal a  -- does not depend on the interpretation f
type Literal a = L (LiteralF a)

Теперь мы можем сделать небольшую грамматику выражений в качестве примера:

data Expr f
    = Lit (f (Literal Integer))
    | Let (f (L Defn)) (f (L Expr))
    | Var (f (L String))
    | Add (f (L Expr)) (f (L Expr))

data Defn f
    = Defn (f (Literal String)) (f (L Expr))

Обратите внимание, как мы передаем метки в f, который, в свою очередь, отвечает за закрытие рекурсии. Если нам просто нужно нормальное дерево выражений, мы можем использовать Tree:

data Tree :: Label -> Type where
    Tree :: h Tree -> Tree (L h)

Тогда Tree (L Expr) изоморфно обычному дереву выражений, которое вы ожидаете. Но это также позволяет нам, например, аннотировать дерево аннотацией, зависящей от метки, на каждом уровне дерева:

data Annotated :: (Label -> Type) -> Label -> Type where
    Annotated :: ann (L h) -> h (Annotated ann) -> Annotated ann (L h)

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

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

person luqui    schedule 16.12.2018
comment
Да, это, наверное, именно то, что я ищу! Для переписывания вывода типов Lamdu и работы над его языком я занимаюсь созданием библиотеки AST., и я обнаружил, что мне нужна такая же гибкость для фиксированных точек (в настоящее время называемых Knots в библиотеке), как и для AST, что очевидно, например, в неидеальных интерфейсах в AST.Class.ZipMatch. Я начал эксперименты, основанные на семействах типов, но если этот подход работает, он действительно выглядит намного чище! Я обновлю здесь :) - person yairchu; 17.12.2018
comment
Похоже, что ваше использование GADT заменяет мое использование семейств типов, и Label, которые я хочу, кажутся data Label = Label (Label -> Type). Я попробую и обновлю. - person yairchu; 17.12.2018
comment
Обновлено, что я использовал типы данных, как вы предложили, в сочетании с семейством типов, а не с GADT, и теперь это существует в github.com/lamdu/syntax-tree . В моей формулировке AST (Expr, DefN) являются узлами точно так же, как Annotated, и для обоих применяется один и тот же класс типов Children. Меня вполне устраивает это решение. Например, это действительно упростило мой класс ZipMatch, а также должно разрешить вывод типов для совместного использования разрешенных терминов унификации (чего я не мог сделать, когда узел UTerm не мог иметь чистые деревья в качестве дочерних элементов). - person yairchu; 17.12.2018
comment
Хотели бы вы, чтобы я отредактировал ваш ответ, чтобы добавить его суть сверху (что решение - это типы данных в сочетании либо с GADT, либо с семействами типов), а затем принять его, или вы предпочитаете редактировать его самостоятельно, или вы предпочитаете, чтобы Я добавляю новый ответ? - person yairchu; 17.12.2018
comment
@yairchu в этом случае кажется подходящим новый ответ, но делайте то, что считаете лучшим. Я рад, что это работает хорошо для вас. - person luqui; 18.12.2018

С типами данных мы можем использовать обычный новый тип:

import Data.Kind (Type)

newtype Inf = Inf (Inf -> Type)

И продвигайте его (с помощью '), чтобы создавать новые виды для представления циклов:

{-# LANGUAGE DataKinds #-}

type F x = x ('Inf x)

Чтобы тип распаковал свой аргумент 'Inf, нам нужно семейство типов:

{-# LANGUAGE TypeFamilies #-}

type family RunInf (x :: Inf) :: Inf -> Type
type instance RunInf ('Inf x) = x

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

  • Спасибо @luqui за то, что указал на часть DataKinds в своем ответе!
person Community    schedule 18.12.2018

Я думаю, вы ищете Fix, который определяется как

data Fix f = Fix (f (Fix f))

Fix дает вам фиксированную точку типа f. Я не уверен, что вы пытаетесь сделать, но такие бесконечные типы обычно используются, когда вы используете схемы рекурсии (шаблоны рекурсии, которые вы можете использовать), см. пакет recursion-schemes Эдварда Кметта или бесплатные монады, которые, среди прочего, позволяют вам построить AST в монадическом стиле.

person madgen    schedule 16.12.2018
comment
К сожалению, Fix это не то, что я ищу. Я пытаюсь сделать что-то более общее, чем recursion-schemes (по крайней мере, в некотором смысле), и мне действительно нужен тип, чтобы получить что-то в своем роде (или его представитель), а не рекурсивно применяемый. - person yairchu; 16.12.2018
comment
@yairchu Вы уверены, что Fix + DataKinds - это не то, что вам нужно? В частности, попробуйте :k 'Fix в ghci и увидите 'Fix :: forall (f :: * -> *). f (Fix f) -> Fix f -- вы получите новый вид, а не *. - person Daniel Wagner; 16.12.2018
comment
@DanielWagner: Как и recursion-schemes, мне нужен способ построения AST, за исключением того, что я хочу, чтобы мои AST могли ссылаться друг на друга - то есть термин может содержать тип (для лямбда-параметра), тип может содержать строку- введите его и наоборот. Я бы хотел, чтобы AST были определены с помощью внешней фиксированной точки (чтобы можно было иметь чистые выражения или выражения, аннотированные типами после вывода типа), но я также хочу, чтобы эти фиксированные точки могли содержать другие типы фиксированных точек. точек (точно так же, как термины могут содержать термины разных типов). Я не понимаю, как Fix мне там помогает - person yairchu; 17.12.2018