如何为Pair2实现支持泛型单参数函数的Functor特性?
异构对Pair2的Functor特性改造
我们已经在Rust中为Option、Identity、Pair等类型实现了Functor特性:Pair的fmap方法可接收单参数泛型函数,并将其作用于两个同类型元素。但异构对类型Pair2<T,E>的当前fmap仅支持接收元组参数的函数,需要修改其Functor实现,使其能像Pair一样接收单参数泛型函数(如id、Some),自动分别作用于两个不同类型的元素:
- 调用
Pair2(4,6.8).fmap(id)返回Pair2(4,6.8) - 调用
Pair2(4,6.8).fmap(Some)返回Pair2(Some(4),Some(6.8))
现有代码
trait Lift<A, B> { type Source; type Target1; } impl<A, B> Lift<A, B> for Option<A> { type Source = Self; type Target1 = Option<B>; } trait Functor<A, B>: Lift<A, B> { fn fmap<F>(self, f: F) -> <Self as Lift<A, B>>::Target1 where F: Fn(A) -> B; } impl<A, B> Functor<A, B> for Option<A> { fn fmap<F>(self, f: F) -> <Self as Lift<A, B>>::Target1 where F: Fn(A) -> B, { self.map(f) } } #[derive(Copy, Clone, Debug)] struct Identity<T>(T); impl<A, B> Lift<A, B> for Identity<A> { type Source = Self; type Target1 = Identity<B>; } impl<A, B> Functor<A, B> for Identity<A> { fn fmap<F>(self, f: F) -> <Self as Lift<A, B>>::Target1 where F: Fn(A) -> B, { Identity(f(self.0)) } } #[derive(Copy, Clone, Debug)] struct Pair<T>(T,T); impl<A, B> Lift<A, B> for Pair<A> { type Source = Self; type Target1 = Pair<B>; } impl<A, B> Functor<A, B> for Pair<A> { fn fmap<F>(self, f: F) -> <Self as Lift<A, B>>::Target1 where F: Fn(A) -> B, { Pair(f(self.0),f(self.1)) } } #[derive(Copy, Clone, Debug)] struct Pair2<T,E>(T,E); impl<A,D, B,C> Lift<(A,C), (B,D)> for Pair2<A,C> { type Source = Self; type Target1 = Pair2<B,D>; } //Can you even implement a functor that takes a generic function? impl<A, B,C,D> Functor<(A,C), (B,D)> for Pair2<A,C> { fn fmap<F>(self, f: F) -> <Self as Lift<(A,C), (B,D)>>::Target1 where F: Fn((A,C)) -> (B,D), { let a=f((self.0,self.1)); Pair2(a.0,a.1) } } fn main() { let a=Some(5); println!("{:?}\n{:?}",a,a.fmap(|a| a+2)); let a=Identity(5); println!("{:?}\n{:?}",a,a.fmap(|a| a+2)); let a=Pair(4,7); println!("{:?}\n{:?}",a,a.fmap(|a| a+2)); let a=Pair2(4,6.8); //challenge, make a fmap that takes a variadic a and adds 2 or something like that like in just "Pair" println!("{:?}\n{:?}",a,a.fmap(|a| (a.0+2,a.1+3.4))); }
当前运行输出
Some(5) Some(7) Identity(5) Identity(7) Pair(4, 7) Pair(6, 9) Pair2(4, 6.8) Pair2(6, 10.2)
改造方案
核心思路是调整Pair2的Functor实现约束,让传入的单参数函数能分别作用于两个异构元素。我们需要让函数F同时实现针对Pair2两个元素类型的映射:
修改后的完整代码
trait Lift<A, B> { type Source; type Target1; } impl<A, B> Lift<A, B> for Option<A> { type Source = Self; type Target1 = Option<B>; } trait Functor<A, B>: Lift<A, B> { fn fmap<F>(self, f: F) -> <Self as Lift<A, B>>::Target1 where F: Fn(A) -> B; } impl<A, B> Functor<A, B> for Option<A> { fn fmap<F>(self, f: F) -> <Self as Lift<A, B>>::Target1 where F: Fn(A) -> B, { self.map(f) } } #[derive(Copy, Clone, Debug)] struct Identity<T>(T); impl<A, B> Lift<A, B> for Identity<A> { type Source = Self; type Target1 = Identity<B>; } impl<A, B> Functor<A, B> for Identity<A> { fn fmap<F>(self, f: F) -> <Self as Lift<A, B>>::Target1 where F: Fn(A) -> B, { Identity(f(self.0)) } } #[derive(Copy, Clone, Debug)] struct Pair<T>(T,T); impl<A, B> Lift<A, B> for Pair<A> { type Source = Self; type Target1 = Pair<B>; } impl<A, B> Functor<A, B> for Pair<A> { fn fmap<F>(self, f: F) -> <Self as Lift<A, B>>::Target1 where F: Fn(A) -> B, { Pair(f(self.0),f(self.1)) } } #[derive(Copy, Clone, Debug)] struct Pair2<T,E>(T,E); // 调整Lift实现,适配异构类型的映射 impl<T, E, U, V> Lift<(T, E), (U, V)> for Pair2<T, E> { type Source = Self; type Target1 = Pair2<U, V>; } // 修改Functor实现,允许函数分别作用于两个异构元素 impl<T, E, U, V> Functor<(T, E), (U, V)> for Pair2<T, E> { fn fmap<F>(self, f: F) -> Self::Target1 where // 约束函数F能同时处理T和E类型,分别映射为U和V F: Fn(T) -> U + Fn(E) -> V, { Pair2(f(self.0), f(self.1)) } } fn main() { let a=Some(5); println!("{:?}\n{:?}",a,a.fmap(|a| a+2)); let a=Identity(5); println!("{:?}\n{:?}",a,a.fmap(|a| a+2)); let a=Pair(4,7); println!("{:?}\n{:?}",a,a.fmap(|a| a+2)); let a=Pair2(4,6.8); // 测试id函数 println!("{:?}\n{:?}",a,a.fmap(|x| x)); // 测试Some函数 println!("{:?}\n{:?}",a,a.fmap(Some)); // 测试数值运算函数 println!("{:?}\n{:?}",a,a.fmap(|x| x + 2)); }
关键修改点
- 调整函数约束:将
Pair2的fmap方法中函数F的约束从Fn((A,C)) -> (B,D)改为Fn(T) -> U + Fn(E) -> V,让函数能分别作用于两个异构元素。 - 适配泛型函数:像
id(|x|x)、Some这类多态单参数函数,天然满足同时处理不同类型的约束,因此可以直接传入fmap。
改造后运行输出
Some(5) Some(7) Identity(5) Identity(7) Pair(4, 7) Pair(6, 9) Pair2(4, 6.8) Pair2(4, 6.8) Pair2(4, 6.8) Pair2(Some(4), Some(6.8)) Pair2(4, 6.8) Pair2(6, 8.8)
内容的提问来源于stack exchange,提问作者Rainb
相关产品推荐
相关产品推荐

