我不明白“类型级编程”是什么意思,我也无法使用Google找到合适的解释。
有人可以提供演示类型级编程的示例吗?范式的解释和/或定义将是有用和赞赏的。
答案 0 :(得分:30)
您已经熟悉"价值水平"编程,您可以操纵42 :: Int
或'a' :: Char
等值。在Haskell,Scala等许多语言中,类型级编程允许您操作Int :: *
或Char :: *
等类型,其中*
是类的类型具体类型(Maybe a
或[a]
是具体类型,但不是具有Maybe
种类的[]
或* -> *
。
考虑这个功能
foo :: Char -> Int
foo x = fromEnum x
此处foo
获取类型 Char
的值,并使用{{1}返回类型为Int
的新值} Enum
的实例。此函数操纵值。
现在将Char
与此类型系列进行比较,并使用foo
语言扩展程序启用。
TypeFamilies
此处type family Foo (x :: *)
type instance Foo Char = Int
使用类 Foo
的类型并使用简单映射返回新类型的*
{ {1}}。这是一个操纵类型的类型级函数。
这是一个非常简单的例子,您可能想知道这可能是多么有用。使用更强大的语言工具,我们可以开始在类型级别编码代码正确性的证明(有关详细信息,请参阅Curry-Howard对应关系)。
一个实际的例子是一个红黑树,它使用类型级编程来静态地保证树的不变量保持不变。
红黑树具有以下简单属性:
我们将使用*
和Char -> Int
,这是一种非常强大的类型级别编程组合。
DataKinds
首先,表示颜色的某些类型。
GADTs
这定义了一种新的{-# LANGUAGE DataKinds, GADTS, KindSignatures #-}
import GHC.TypeLits
种,有两种类型:data Colour = Red | Black -- promoted to types via DataKinds
和Colour
。请注意,这些类型中没有值(忽略底部),但我们无论如何都不需要它们。
红黑树节点由以下Red
Black
GADT
可让我们直接在类型中表达-- 'c' is the Colour of the node, either Red or Black
-- 'n' is the number of black child nodes, a type level Natural number
-- 'a' is the type of the values this node contains
data Node (c :: Colour) (n :: Nat) a where
-- all leaves are black
Leaf :: Node Black 1 a
-- black nodes can have children of either colour
B :: Node l n a -> a -> Node r n a -> Node Black (n + 1) a
-- red nodes can only have black children
R :: Node Black n a -> a -> Node Black n a -> Node Red n a
和GADT
构造函数的Colour
。
树的根看起来像这样
R
现在不可能创建一个违反上述4个属性中任何一个的良好类型B
。
data RedBlackTree a where
RBTree :: Node Black n a -> RedBlackTree a
。RedBlackTree
的定义,根是黑色的。Colour
构造函数的定义,所有叶子都是黑色的。 RedBlackTree
构造函数的定义来看,它的两个孩子都必须这样做
是Leaf
个节点。同样,每个子树的黑色子节点数相等(在左右子树的类型中使用相同的R
)GHC在编译时检查所有这些条件,这意味着我们永远不会从一些行为不当的代码中获得运行时异常,从而使我们对红黑树的假设无效。重要的是,没有与这些额外好处相关的运行时成本,所有工作都在编译时完成。
答案 1 :(得分:23)
在大多数静态类型语言中,您有两个"域名"价值水平和类型水平(某些语言甚至更多)。类型级编程涉及在编译时评估的类型系统中的编码逻辑(通常是函数抽象)。一些例子是模板元编程或Haskell类型族。
在Haskell中执行此示例需要一些语言扩展,但您现在暂时忽略它们,只是将类型族视为函数,而不是类型级数(Nat
)。 / p>
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
import GHC.TypeLits
import Data.Proxy
-- value-level
odd :: Integer -> Bool
odd 0 = False
odd 1 = True
odd n = odd (n-2)
-- type-level
type family Odd (n :: Nat) :: Bool where
Odd 0 = False
Odd 1 = True
Odd n = Odd (n - 2)
test1 = Proxy :: Proxy (Odd 10)
test2 = Proxy :: Proxy (Odd 11)
此处不是测试自然数值是否为奇数,而是测试自然数类型是否为奇数并将其减少为编译时的类型级布尔值。如果您评估此程序,test1
和test2
的类型将在编译时计算为:
λ: :type test1
test1 :: Proxy 'False
λ: :type test2
test2 :: Proxy 'True
这是类型级编程的本质,取决于您可能能够在类型级别编码具有各种用途的复杂逻辑的语言。例如,限制值级别的某些行为,管理资源最终化,或存储有关数据结构的更多信息。
答案 2 :(得分:12)
其他答案非常好,但我想强调一点。我们的术语的编程语言理论主要基于Lambda微积分。 A"纯粹" Lisp对应于(或多或少)严重加糖的无类型Lambda微积分。程序的含义由评估规则定义,该规则说明在程序运行时如何减少Lambda微积分项。
在键入的语言中,我们为术语分配类型。对于每个评估规则,我们都有一个相应的类型规则,显示如何通过评估保留类型。根据类型系统,还有其他规则定义类型彼此之间的关系。事实证明,一旦你获得了一个足够有趣的类型系统,类型及其规则系统也对应于Lambda微积分的变体!
虽然现在将Lambda Calculus视为一种编程语言很常见,但最初它被设计为逻辑系统。这就是为什么它可以用于推理编程语言中的术语类型。但Lambda Calculus的编程语言方面允许人们编写由类型检查器评估的程序。
希望您现在可以看到"类型级编程"与术语级编程并不是完全不同的事情,现在在类型系统中拥有足够强大的语言并不常见您有理由在其中编写程序。