Как вы формулируете N-арные типы произведений и сумм в этой типизированной Вселенной лямбда-исчисления?
вот код, где у меня проблема:
{-# LANGUAGE GADTs, LANGUAGE DataKinds #-}
-- * Universe of Terms * --
type Id = String
data Term a where
Var :: Id -> Term a
Lam :: Id -> Type -> Term b -> Term (a :-> b)
App :: Term (a :-> b) -> Term a -> Term b
Let :: Id -> Term a -> Term b -> Term b
Tup :: Term a -> Term b -> Term (a :*: b) -- * existing tuple
Lft :: Term a -> Term (a :+: b) -- * existing sum
Rgt :: Term b -> Term (a :+: b)
Tru :: Term Boolean
Fls :: Term Boolean
Bot :: Term Unit
-- * Universe of Types * --
data Type = Type :-> Type | Type :*: Type | Type :+: Type | Boolean | Unit
поэтому я хочу продлить Tup
быть определенным над произвольно многими аргументами, то же самое с sum. Но формулировка, включающая списки, ограничила бы окончательный термин одним типом a:
Sum :: [Term a] -> Term a
я мог бы просто избавиться от a
и сделать что-то вроде:
Sum :: [Term] -> Term
но тогда я теряю те самые вещи, которые я пытаюсь выразить.
Итак, как я могу выразить некоторый полиморфный термин без потеря выразительности?
1 ответов
делать это для "списка" сложно, используя систему типов Haskell, но это можно сделать. В качестве отправной точки достаточно легко, если вы ограничиваете себя двоичными продуктами и суммами (и лично я бы просто придерживался этого):
{-# LANGUAGE GADTs, DataKinds, TypeOperators, KindSignatures, TypeFamilies #-}
import Prelude hiding (sum) -- for later
-- * Universe of Terms * --
type Id = String
data Term :: Type -> * where
Var :: Id -> Term a
Lam :: Id -> Type -> Term b -> Term (a :-> b)
App :: Term (a :-> b) -> Term a -> Term b
Let :: Id -> Term a -> Term b -> Term b
Tup :: Term a -> Term b -> Term (a :*: b) -- for binary products
Lft :: Term a -> Term (a :+: b) -- new for sums
Rgt :: Term b -> Term (a :+: b) -- new for sums
Tru :: Term Boolean
Fls :: Term Boolean
Uni :: Term Unit -- renamed
-- * Universe of Types * --
data Type = Type :-> Type | Type :*: Type | Type :+: Type | Boolean | Unit | Void
-- added :+: and Void for sums
чтобы построить тип суммы произвольной длины, нам нужна среда терминов. Это гетерогенный список, индексированный по типам терминов в нем:
data Env :: [Type] -> * where
Nil :: Env '[]
(:::) :: Term t -> Env ts -> Env (t ': ts)
infixr :::
затем мы используем семейство типов, чтобы Свернуть список типов в двоичный файл вид продукции.
Кроме того, мы могли бы добавить что-то вроде Product [Type]
до Type
Вселенной.
type family TypeProd (ts :: [Type]) :: Type
type instance TypeProd '[] = Unit
type instance TypeProd (t ': ts) = t :*: TypeProd ts
на prod
функции сворачивает такую среду для приложений Tup
. Опять ты
также можно добавить Prod
как конструктор этого типа Term
тип данных.
prod :: Env ts -> Term (TypeProd ts)
prod Nil = Uni
prod (x ::: xs) = x `Tup` prod xs
суммы произвольной длины принимают только один элемент для инъекции, но нужен тег для указания в какой тип суммы его ввести:
data Tag :: [Type] -> Type -> * where
First :: Tag (t ': ts) t
Next :: Tag ts s -> Tag (t ': ts) s
опять же, у нас есть введите family и функцию для создания такого зверя:
type family TypeSum (ts :: [Type]) :: Type
type instance TypeSum '[] = Void
type instance TypeSum (t ': ts) = t :+: TypeSum ts
sum :: Tag ts t -> Term t -> Term (TypeSum ts)
sum First x = Lft x
sum (Next t) x = Rgt (sum t x)
конечно, возможно множество вариаций или обобщений, но это должно дать вам идея.