TL; DR:我想使用type family RIso (a :: [*]) (b :: [*]) :: Bool
实现行多态性的统一,但是陷入困境。
我正在为一种语言编写一种编译器,该语言的效果系统使用行多态性(如Extensible Records with Scoped Labels中那样)来表示附加到函数类型的效果集。也就是说,用这种语言,功能箭头的种类是:
(->) :: * -> * -> Effect -> *
其中Effect
是一系列效果标签,例如IO
或Unsafe
-标签与另一个效果行的“ 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
,以及从A
到B
的函数,并具有一组效果{{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′ }
,l
和r
以及统一规则,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’
;其次,我打算使用这种机制来实现该语言的可扩展记录和变体。
答案 0 :(得分:1)
是否可以引入带有forall或其他内容的新鲜变量
不幸的是没有。那就是不可思议性,在Haskell中就没有很好的故事。
通过类型族进行的计算将在检查变量后立即卡住。您必须将来宾语言的变量(行)编码为具体类型,才能对其进行任何操作。