Haskell中有态射吗?
我有一些GADT,它代表lambda演算中的一个术语Haskell中有态射吗?,haskell,functional-programming,functor,Haskell,Functional Programming,Functor,我有一些GADT,它代表lambda演算中的一个术语 data Term a = Var a | Lambda a (Term a) | Apply (Term a) (Term a) 我想做的是为这种类型的转换提供一个通用接口。其类型签名应与以下内容类似: (Term a -> Term a) -> Term a -> Term a 编写此函数很容易: fmap' :: (Term a → Term a) → Term a → Term a
data Term a =
Var a
| Lambda a (Term a)
| Apply (Term a) (Term a)
我想做的是为这种类型的转换提供一个通用接口。其类型签名应与以下内容类似:
(Term a -> Term a) -> Term a -> Term a
编写此函数很容易:
fmap' :: (Term a → Term a) → Term a → Term a
fmap' f (Var v) = f (Var v)
fmap' f (Lambda v t) = Lambda v (fmap' f t)
fmap' f (Apply t1 t2) = Apply (fmap' f t1) (fmap' f t2)
所以,我的问题是,在haskell(或haskell库)中是否有某种通用结构来进行这种转换(类似于
函子
,它可能被称为态射)?使用你在问题中引用的fmap'
,转换函数f
只能将Var
值转换为不同的lambda表达式。它无法将Lambda
或Apply
转换为其他内容,因为在fmap'
函数中对其进行了硬编码,使这些构造函数保持不变
例如,您的fmap'
可以将Var 1
转换为Lambda 1(Var 1)
,但不能反过来。
这真的是你的意图吗
如果fmap'
应该允许任意转换,那么您将得到以下结果:
fmap' :: (Term a → Term a) → Term a → Term a
fmap' f t = f t
这与$
相同
(另一方面,如果只允许
fmap'
更改表达式中的值,而不更改其结构,则您将回到通常的fmap
)首先,我对您的实际问题没有答案,尽管我有一些可能对您有用的想法,但我不确定
在我看来,这似乎没有它可能是一般性的,我希望您使用以下内容:
fmap' :: (Term a → Term a) → Term a → Term a
fmap' f (Var v) = f $ Var v
fmap' f (Lambda v t) = f $ Lambda v (fmap' f t)
fmap' f (Apply t1 t2) = f $ Apply (fmap' f t1) (fmap' f t2)
这仍然允许您使用相同的功能,您只需在f本身中进行模式匹配。我认为这个函数可以用来计算lambda演算(不过,你需要带上一些状态)
将来对您更有用的可能是:
fmapM :: Monad m => (Term a -> m (Term a)) -> Term a -> m (Term a)
fmapM f (Var v) = f (Var v)
fmapM f (Lambda v t) = do
t' <-fmapM f t
f (Lambda v t')
fmapM f (Apply t1 t2) = do
t1' <- fmapM f t1
t2' <- fmapM f t2
f (Apply t1' t2')
fmapM::Monad m=>(术语a->m(术语a))->术语a->m(术语a)
fmapM f(Var v)=f(Var v)
fmapM f(λv t)=do
t'Identity a(
让我知道这是否有用:)请查看。它可以进行您所说的那种转换,但对于任意数据结构来说都是如此。以下是术语供参考
data Term a =
Var a
| Lambda a (Term a)
| Apply (Term a) (Term a)
(我注意到变量的表示是抽象的,这通常是一个很好的计划。)
…下面是建议的功能
fmap' :: (Term a → Term a) → Term a → Term a
fmap' f (Var v) = f (Var v)
fmap' f (Lambda v t) = Lambda v (fmap' f t)
fmap' f (Apply t1 t2) = Apply (fmap' f t1) (fmap' f t2)
这个定义让我烦恼的是,f
只适用于形式术语(Var v)
,因此您最好实现替换
如果您稍微注意区分绑定变量和自由变量,您将能够使用替换实现(>>=)
,使术语成为单子。一般来说,术语可以具有用于重命名的函子
结构和用于替换的单子
结构。关于这一点有一篇很好的文章,但我离题了
同时,如augustss所建议的,如果您不想在变量处执行操作,一种通用方法是使用uniplate之类的通用遍历工具包。另一种可能,也许更严格一点,是为你的类型使用“折叠”
tmFold :: (x -> y) -> (x -> y -> y) -> (y -> y -> y) -> Term x -> y
tmFold v l a (Var x) = v x
tmFold v l a (Lambda x t) = l x (tmFold v l a t)
tmFold v l a (Apply t t') = a (tmFold v l a t) (tmFold v l a t')
这里,v
、l
和a
为您的术语
形成操作定义一个替代代数,仅作用于y
,而不是术语x
,解释如何处理变量、lambda和应用程序。您可以选择y
作为m(术语x)
以获得一些合适的monadm
(例如,为变量线程化环境),而不仅仅是术语x
本身。处理每个子项以给出一个y
,然后选择适当的函数为整个项生成y
。折叠捕获标准递归模式
普通的一阶数据类型(以及一些表现良好的高阶数据类型)都可以配备折叠运算符。以可读性为代价,您甚至可以一次性编写折叠操作符
data Fix f = In (f (Fix f))
fixFold :: Functor f => (f y -> y) -> Fix f -> y
fixFold g (In xf) = g (fmap (fixFold g) xf)
data TermF a t
= VarF a
| LambdaF a t
| ApplyF t t
type Term a = Fix (TermF a)
与递归的术语a
不同,此术语f a t
说明了如何将t
元素放在子术语的位置,构成术语的一层。我们使用递归Fix
类型返回递归术语
结构。我们在装饰上少了一点,因为每一层都有一个额外的。我们可以定义
var x = In (VarF x)
lambda x t = In (LambdaF x t)
apply t t' = In (Apply x t t')
但我们不能在模式匹配中使用这些定义。不过,这样做的好处是,我们可以使用通用的fixFold
,而不需要额外的成本。要从一个项计算ay
,我们只需要给出一个类型的函数
TermF a y -> y
这(就像上面的v
、l
和a
一样)解释了如何处理其子项已被处理为y
类型值的任何术语。通过明确一个层由什么组成的类型,我们可以一层一层地了解工作的一般模式。您可能会发现该包很有用,尤其是函数。因此,您基本上是在问像类Morphism m where fmap':(m a→ (硕士)→ 我是→ m a
已经存在(但可能有不同的名称)?@stakx,对,这正是我要找的。我不太明白fmap'
的目的是什么。。。给定签名的最简单实现不是只有id
?@Matthias,id
只接受一个参数,而fmap'
是一个二进制操作。重要的一点似乎是fmap'
操作并仅返回一种类型的值;这就是它与例如($)
@stakx的区别:fmap'
是一元函数,就像其他Haskell函数一样<代码>(硕士)→ (硕士)→ 我是→ MA与(MA)相同→ (硕士)→ (硕士)→ m a)
,它是id
类型的一个实例。具体来说,这种转换可以通过
fmap' :: (Term a → Term a) → Term a → Term a
fmap' f (Var v) = f (Var v)
fmap' f (Lambda v t) = Lambda v (fmap' f t)
fmap' f (Apply t1 t2) = Apply (fmap' f t1) (fmap' f t2)