类型级别列表的行多态相等性

时间:2018-06-27 00:48:42

标签: haskell type-systems type-families type-level-computation

TL; DR:我想使用type family RIso (a :: [*]) (b :: [*]) :: Bool实现行多态性的统一,但是陷入困境。


背景

我正在为一种语言编写一种编译器,该语言的效果系统使用行多态性(如Extensible Records with Scoped Labels中那样)来表示附加到函数类型的效果集。也就是说,用这种语言,功能箭头的种类是:

(->) :: * -> * -> Effect -> *

其中Effect是一系列效果标签,例如IOUnsafe-标签与另一个效果行的“ cons”,效果类型变量或“纯”效果(空白行)。一个简单的例子是map的类型:

<A, B, +E> (List<A>, (A -> B +E) -> List<B> +E)

使用假设的Haskell语法:

forall (a :: *) (b :: *) (e :: Effect).
  (a -> Eff e b) -> [a] -> Eff e [b]

这意味着map接受类型为List的值的A,以及从AB的函数,并具有一组效果{{1 }},并返回+E中的List,也需要效果B。纯函数的作用是完全多态的,默认情况下为空行;因此+E是纯函数,如果其功能参数是纯函数;否则它需要与参数相同的效果。

我正在尝试在Haskell的类型系统中表示这种语言的类型系统,以便我可以依靠Haskell的类型检查器以this blog post的方式编写经过验证的类型检查器。

但是,当我要统一函数类型时,我需要通过常规的统一(等式)来统一输入和输出,但是我需要通过 row unification 来统一效果。所以:

map

这表示“如果s ≃ { l | s′ } : θ₁ tail(r) ∉ dom(θ₁) -- † θ₁(r) ~ θ₁(s′) : θ₂ ---------------------------------------- [uni-row] { l | r } ~ s : θ₂ ∘ θ₁ 在替换s下可以重写为{ l | s′ },并且θ₁r在替换s′下可以统一,然后θ₂在替换{ l | r }下与s统一,外加一个带有匕首(†)的副条件,我将在稍后进行解释。

(≃)关系表示,根据以下三个规则,两行是同构

θ₂ ∘ θ₁

在我当前的 unverified 类型检查器中,这两个函数在Haskell中如下所示:

-------------------------- [row-head]
{ l | r } ≃ { l | r } : []

l ≠ l′
r ≃ { l | r′ } : θ
-------------------------------- [row-swap]
{ l′ | r } ≃ { l | l′ | r′ } : θ

fresh(β)
----------------------------- [row-var]
α ≃ { l | β } : [α ↦ {l | β}]

unify typeEnv0 (Row l r) s = do -- Attempt to rewrite. maybeSubstitution <- isomorphicRows typeEnv0 l s (rowLast r) case maybeSubstitution of -- If rows are isomorphic: Just (s', substitution, typeEnv1) -> case substitution of -- Apply substitution, if any, and unify the tails. Just (x, t) -> let typeEnv2 = typeEnv1 { typeEnvVars = Map.insert x t $ typeEnvVars typeEnv1 } in unify typeEnv2 r s' Nothing -> unify typeEnv1 r s' -- Non-isomorphic rows do not unify. Nothing -> reportTypeMismatch (Row l r) s -- Gets the last element in a row. rowLast :: Type -> Type rowLast (Row _ r) = rowLast r rowLast t = t 实现了本文中描述的规则:

  

当行isomorphicRows与行{ l | r }统一时,我们首先尝试以s的形式重写s […]如果成功,则统一进行通过统一…行尾。

因此,给定{ l | s′ }lr以及统一规则,s用于断言isomorphicRows可以重写为{ {1}}在某种替换下,失败(s)或返回({ l | r })重写行的尾部,替换(Nothing因为它始终为空或单例),并且更新的类型环境。我们实际上并没有通过Just,因为我们只需要其最后一个元素Maybe

r

rt规则:以给定标签开头的行将通过标识替换来简单地重写。

isomorphicRows
  :: TypeEnv    -- Type environment
  -> Type       -- Effect label l
  -> Type       -- Effect row s
  -> Type       -- Effect row rt
  -> Typecheck
    (Maybe
      ( Type
      , Maybe (TypeId, Type)
      , TypeEnv
      ))

[row-head]规则:其中包含标签的行可以被重写以将该标签放置在头部。

isomorphicRows typeEnv0 l (Row l' r') _rt
  | l == l'
  = pure $ Just (r', Nothing, typeEnv0)

[row-swap]规则:与类型变量统一时,不存在标签,因此我们无法直接测试相等性,并且必须为行尾返回一个新鲜的变量。

  

†这将强制执行附带条件,以防止统一具有尾巴但前缀不同的行,从而即使我们添加了新的类型变量也可以确保终止。

isomorphicRows typeEnv0 l (Row l' r') rt
  | l /= l' = do
  maybeSubstitution <- isomorphicRows typeEnv0 l r' rt
  pure $ case maybeSubstitution of
    Just (r'', substitution, typeEnv1) -> Just
      (Row l r'', substitution, typeEnv1)
    Nothing -> Nothing

在其他情况下,这些行不是同构的。

[row-var]

我尝试过的东西

我正在尝试使用对类型的类型级别列表进行操作的类型族将isomorphicRows typeEnv0 l r@(TypeVar a) rt | r /= rt = do (b, typeEnv1) <- freshTypeVar typeEnv0 pure $ Just (b, Just (a, Row l b), typeEnv1) 的行统一转换为类型级别:

isomorphicRows _ _ _ _ = Nothing

这似乎适用于基本情况:

isomorphicRows

但是在更复杂的情况下失败:

type family RIso (ra :: [*]) (rb :: [*]) :: Bool where

  -- [row-head]
  RIso ((l) ': r) ((l) ': s) = RIso r s

  -- [row-swap]
  RIso ((l) ': (l') ': r) ((l') ': (l) ': r)
    = TNot (TEq l l')

  RIso r r = 'True

  RIso _ _ = 'False

type family TNot (a :: Bool) :: Bool where
  TNot 'False = 'True
  TNot 'True = 'False

type family TEq (a :: Bool) (b :: Bool) :: Bool where
  TEq a a = 'True
  TEq _ _ = 'False

我相信是因为我可能没有正确实施> :kind! RIso '[] '[] 'True > :kind! RIso '[Int] '[Int] 'True > :kind! RIso '[Int, Char] '[Char, Int] 'True > :kind! RIso '[Int, Char] '[Int, String] 'False > :kind! RIso '[Int, Char, String] '[Char, Int, String] 'True 规则(> :kind! RIso '[Int, String, Char] '[Char, Int, String] 'False 似乎可疑),并且我错过了[row-swap]规则。我坚持执行TNot,因为我不知道如何将新鲜的类型变量引入范围(或者这是否是正确的方法)。我尝试过:

[row-var]

但是看到免费的[row-var]会产生类型错误,我并不感到惊讶:

type family REq (ra :: [*]) (rb :: [*]) :: Bool where
  …
  -- [row-var]
  REq (l ': r) rt = TAnd (TNot (TEq r rt)) (TEq r (Row l b))
  …

type family TAnd (a :: Bool) (b :: Bool) :: Bool where
  TAnd 'True 'True = 'True
  TAnd _ _ = 'False

问题

  • 是否可以使用b或其他内容引入一个新变量,还是我应该放弃尝试直接实现键入规则并采用其他方法的想法?

  • 或者,是否有一些现有的可扩展记录库专门使用行统一(不是其他方法),因此我可以使用该库或从中学习技术?

请注意,这与简单集相等性不相同,因为可能存在重复的标签,并且按照我在此问题顶部链接的论文中描述的意义,它们是“作用域”的。这很重要,原因有两个:首先,我打算在以后进行扩展,以便效果标签可以具有类型参数,例如error: Not in scope: type variable ‘b’ ;其次,我打算使用这种机制来实现该语言的可扩展记录和变体。

1 个答案:

答案 0 :(得分:1)

  

是否可以引入带有forall或其他内容的新鲜变量

不幸的是没有。那就是不可思议性,在Haskell中就没有很好的故事。

通过类型族进行的计算将在检查变量后立即卡住。您必须将来宾语言的变量(行)编码为具体类型,才能对其进行任何操作。