Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell中有态射吗?_Haskell_Functional Programming_Functor - Fatal编程技术网

Haskell中有态射吗?

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

我有一些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 
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)
以获得一些合适的monad
m
(例如,为变量线程化环境),而不仅仅是
术语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
,而不需要额外的成本。要从一个项计算a
y
,我们只需要给出一个类型的函数

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)