我的目标是从术语中删除()
,如下所示:
(a, b) -> (a, b)
((), b) -> b
(a, ((), b)) -> (a, b)
...
这是代码:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Simplify where
import Data.Type.Bool
import Data.Type.Equality
type family Simplify x where
Simplify (op () x) = Simplify x
Simplify (op x ()) = Simplify x
Simplify (op x y) = If (x == Simplify x && y == Simplify y)
(op x y)
(Simplify (op (Simplify x) (Simplify y)))
Simplify x = x
然而,尝试一下:
:kind! Simplify (String, Int)
...导致类型检查器中出现无限循环。我认为If
类型的家庭应该照顾不可约的条款,但我显然遗漏了一些东西。但是什么?
答案 0 :(得分:10)
类型系列评估不是懒惰,因此If c t f
将评估c
,t
和f
的所有内容。 (事实上,类型族评价顺序现在还没有真正定义。)所以难怪你最终会得到一个无限循环 - 你总是评价Simplify (op (Simplify x) (Simplify y))
,即使这样#&# 39; s Simplify (op x y)
!
你可以通过将递归和简化分开来避免这种情况,如下所示:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Simplify where
import Data.Type.Bool
import Data.Type.Equality
type family Simplify1 x where
Simplify1 (op () x) = x
Simplify1 (op x ()) = x
Simplify1 (op x y) = op (Simplify1 x) (Simplify1 y)
Simplify1 x = x
type family SimplifyFix x x' where
SimplifyFix x x = x
SimplifyFix x x' = SimplifyFix x' (Simplify1 x')
type Simplify x = SimplifyFix x (Simplify1 x)
这个想法是:
Simplify1
执行简化步骤。SimplifyFix
需要x
及其一步简化x'
,检查它们是否相等,以及它们是否进行了另一个简化步骤(因此找到Simplify1
)的固定点。Simplify
只需拨打SimplifyFix
即可从Simplify1
链开始。由于类型系列模式匹配是惰性的,SimplifyFix
会正确延迟评估,从而阻止无限循环。
确实:
*Simplify> :kind! Simplify (String, Int)
Simplify (String, Int) :: *
= (String, Int)
*Simplify> :kind! Simplify (String, ((), (Int, ())))
Simplify (String, ((), (Int, ()))) :: *
= ([Char], Int)
答案 1 :(得分:3)
我想我会提到,鉴于简化具有折叠结构,因此无需构建涉及一次又一次重新遍历表达式的修复点的复杂解决方案。
这样做会很好:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Simplify where
type family Simplify x where
Simplify (op a b) = Op op (Simplify a) (Simplify b)
Simplify x = x
type family Op op a b where
Op op () b = b
Op op a () = a
Op op a b = op a b