问题描述
我尝试用JavaScript实现Functor。
Functor的定义图如下:
或在nLab
https://ncatlab.org/nlab/show/functor
在这里,您看到F(f)
表达式在类别图中看起来很典型。
我设法通过JavaScript将Array.map
实现为Functor,如下所示:
const compose = f => g => x => g(f(x));
const f = a => a * 2;
const F = a => [a];
const A = 1;
const FA = F(A); //[1]
const Ff = compose(f)(F);
const FB = Ff([FA]);
console.log(FB); //[2]
F = a => [a]
A = 1
时,
F(1) = [1]
但是,尽管我了解F(f)
的含义,
F(f) = [f]
至少不能在JavaScript中用作函数。
实际上,只有我能想到的适当方法是函数组合,例如:
compose(f)(F)
。
我也是
FB = Ff([FA])
为使它起作用,我认为该表达式仅适用于数组,在其他情况下会出错。
所以,这是我的问题。
尽管我了解F(A)
,F(B)
和F(B)
的建议,但实际上F(A)
,F(B)
无效,{{1 }}是否必须直接组成功能?
或者,在范畴论中,它是否允许将F(f)
和f
的功能组合表示为g
隐式地??
解决方法
最新更新:
您实现的实际上是(a -> b) -> a -> [b]
,但是您欺骗自己以为它是(a -> b) -> [a] -> [b]
(有关更多详细信息,请参见下文)。
在Javascript中进行观察,评估
[Int] * Int
将返回Int
,这可能是所有混乱的起因。
在类型设置中,F(f)
仅在类型系统允许类型变量像“通用”那样的情况下才有意义(例如在Haskell中)
F :: a -> [b]
其中a
可以是Int
和(Int -> Int)
或其他任何东西,b
也可以。而您正在寻找的a
可能是(a0 -> b) -> a0
的情况:
F :: (a0 -> b) -> a0 -> [b]
此处b
为a0
,当您将F . f
放在其中时会发生这种情况
(.) :: (b -> c) -> (a -> b) -> a -> c
c为[b]
,a为a0
。
请注意,这与
不同F0 :: (a -> b) -> [a] -> [b]
它是List的fmap
,而您所知道的Functor
(至少在Haskell中实现)只是具有功能的任何类型F
fmap :: (a -> b) -> F a -> F b
还要注意,在您的示例中,此F
作为函数存在于与F
完全不同的抽象级别上。 (实际上,在Haskell中,您甚至无法将F
定义为一个函数。)
另一方面,在无类型的lambda演算等中,只要您更明确地编写内容(例如,
const F = a => _.isFunction(a) ? x => [a(x)] : [a]
以上是从编程语言理论的角度来看。
或者,在范畴论中,它是否允许将f和g的函数组成隐式表示为g(f)?
据我了解,范畴理论是一种泛泛的功能理论。它本身与语法无关。
如果您想通过一种编程语言的实现来表达范畴论中的一个概念(例如functor),那么客观地讲,它可以归结为语言功能,主观上可以归结为语言用户。
, JavaScript数组的函子实现是Array.map
,它对数组元素采用函数,对数组产生函数。如果f
是某个函数,那么根据图表的分类语言,F(f)
是.map(f)
(请原谅我对符号的滥用)。
在图中,标识和组成并不表示应如何实现函子抽象。相反,这些图表示的是函子定律:具体来说,.map(id)
必须与id
相同,并且.map(f).map(g)
必须与.map(compose(f)(g))
相同
要在JavaScript中组合两个函数,我可以想到三种方式。
第一种方法
最简单的方法是在第二个调用中调用第一个函数。
F(f(x)) // -> result
这仅需要定义两个函数并提供所需的正确参数
第二种方法
您在问题中提出的componse
函数方式,其中您生成了一个将两个函数的结果组合在一起的函数。我会在这里稍微简化一下。
const compose = (f,g) => ((x) => (g(f(x))));
const Ff = compose(f,F);
Ff(x) // -> result exact to F(f(x))
第三种方法
此方法回答您所需的语法(F(f)
)。您可以声明外部函数F
的特殊版本,该特殊版本可以采用原始类型参数并立即计算结果,也可以采用函数参数并返回结合了两者执行的另一个函数。您可以通过定义以下函数生成器composify
来实现此目的。
const composify = (f) => (
(x) => {
if (x instanceof Function) {
return (z) => (f(x(z)));
}
return f(x);
}
);
const Fc = composify(F);
const fc = composify(f);
Fc(x) // -> result exact to F(x)
Fc(fc)(x) // -> result exact to F(f(x))
fc(Fc)(x) // -> result exact to f(F(x))
我了解第三种方法需要将数学函数定义为特殊函数,但是如果您愿意这样做,则可以使用所需的语法在两个方向上执行组合。
这里是一个例子:
const composify = (f) => (
(x) => {
if (x instanceof Function) {
return (z) => (f(x(z)));
}
return f(x);
}
);
const addOne = (x) => (x + 1);
const double = (x) => (x * 2);
const composingAddOne = composify(addOne);
const composingDouble = composify(double);
console.log(composingAddOne(3)); // -> (3 + 1) = 4
console.log(composingDouble(3)); // -> (3 * 2) = 6
console.log(composingDouble(composingAddOne)(3)); // -> (3 + 1) * 2 = 8
console.log(composingAddOne(composingDouble)(3)); // -> (3 * 2) + 1 = 7
,
在这里有很多答案并且对它们不满意之后,我自己解决了。
根据nLab中的图表:
这里,X,Y,Z实际上是同一性态。
所以X,Y,Z和f,g,h的类型相同。
因此,我们可以一致地写F(f)
或F(X)
。
根据此质量检查流程,尽管许多人从未提及过,也不同意我的最初反应,但这似乎是一个已知事实。
https://mathoverflow.net/questions/336533/why-is-an-object-not-defined-as-identity-morphism
https://ncatlab.org/nlab/show/single-sorted+definition+of+a+category
从类别的单分类定义的角度来看,对象X,Y,Z是身份同态。