表达无限种

时间:2018-12-16 19:15:42

标签: haskell types type-families type-kinds

在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具有种类*),因此种类更少安全。

有没有更优雅的解决方案?

3 个答案:

答案 0 :(得分:5)

回复:

  

像递归方案一样,我想要一种构造AST的方法,除了我希望我的AST能够相互引用-即一个术语可以包含一个类型(对于lambda参数),一个类型可以包含一个行类型,反之亦然。我希望用一个外部固定点定义AST(这样,一个人可以有“纯”表达式或在类型推断后用类型注释的表达式),但我也希望这些固定点能够包含其他类型的固定点(就像术语可以包含不同类型的术语一样)。我看不到Fix如何在那里帮助我

我有一个方法,也许与您要的差不多,我一直experimenting with。尽管围绕此构造的抽象需要一些开发,但它似乎非常强大。关键是存在一种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))

请注意我们如何将 labels 传递给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由形状函子而不是标签索引,但是现在看来对我来说更干净。有很多这样的小决定要做出,我还没有找到最方便的模式。围绕形状函子使用的最佳抽象仍然需要探索和发展。

还有许多其他可能性,其中许多我还没有探讨过。如果您最终使用它,我很想听听您的用例。

答案 1 :(得分:2)

对于数据类型,我们可以使用常规的新类型:

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部分!

答案 2 :(得分:1)

我认为您正在寻找被定义为

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

Fix为您提供类型f的固定点。我不确定您要做什么,但是当您使用递归模式(可以使用的递归模式)时,通常会使用这种无限类型(请参见Edward Kmett的recursion-schemes包)或带有免费monad的包。事物使您能够以单子形式构造AST。