我今天偶然发现了一个小问题。考虑一个小包装类:
class Event<T> {
T value;
Class<T> type;
// other fields, getters and setters omitted for brevity
}
现在,我想将Event<Long>
转换为Event<String>
,同时保留其他字段并更新type
成员。
最终我得到了最简单的"解决方案":
Event<String> new = new Event(old.getValue().toString(), String.class, other, fields);
然而,在与Haskell合作过我的宠物项目之后,我自然而然地渴望像fmap :: Functor f => (a -> b) -> f a -> f b
这样的函数(阅读:给定一个从a到b的函数和一个包含a类型的函子,给我一个包含b的结果),在发现没有标准实现后,我开始自己编写一个:
interface Functor<T> {
Functor<S> fmap( Func1<T,S> f );
}
// ... in Event<T>:
Functor<S> fmap( Func1<T,S> f ) {
S newValue = f.call(this.value);
return new Event( newValue, newValue.getClass(), other, fields);
}
现在这个解决方案存在一个问题:在 Java 中调用 fmap 后,我留下了一个类型 Functor<String>
的实例,而 Haskell 中的相同函数将返回一个 Event<String>
。
有没有办法让我的Event
回来(不安全地投射它)?
不,这是不可能的。为此,我们需要在界面中的Functor
上抽象,例如
interface Functor<T> as F<T> {
F<S> map(f : Function<T, S>);
}
但是Java不允许你抽象类型构造函数,只允许类型。这被称为高等类型(HKT)。只有少数(非依赖)语言有HKT,Scala和Haskell是我能想到的仅有的两种。
事实上,HKTs对于表达很多抽象是必要的,
- 控制莫纳德
- 控制.应用
- 数据可遍历
- 数据.可折叠
- Control.Monad.Trans
- 任何单变压器,全挡
- 免费单子
- 透镜/棱镜
- 流库(它们是单元转换器)
- 几乎所有的东西都在
category-extras
所有这些都涉及对类型构造函数的抽象,而不仅仅是具体类型。
对我来说效果很好,但还不是很Functor<T>
. 它还要求您指定 F
,您的函子是其实例的类型:
interface Fn1<A, B> {
B apply(A a);
}
interface Functor<A, F extends Functor<?, ?>> {
<B> F map(Fn1<A, B> f);
}
您的Event<A>
类将实现如下Functor
:
public class Event<A> implements Functor<A, Event<?>> {
public final A value;
public Event(A _value) {
value = _value;
}
public <B> Event<B> map(Fn1<A, B> f) {
return new Event<B>(f.apply(value));
}
public String toString() {
return "Event<" + value.getClass().getSimpleName() + ">(" + value.toString() + ")";
}
}
如果您需要闭合到扩展类的函子(例如 Observable
来自 rxjava),你可以编写一个看起来更像类型类的东西,但是它不能符合任何类似函子的接口,因为 Java 缺乏更高级的类型:
public class ObservableFunctor {
public static <A,B> Observable<B> map(Observable<A> fa, Fn1<A, B> f) {
return new Observable<B>(f.apply(fa.value));
}
}
下面是一个可运行的示例,它同时使用上面的Event
和Observable
:
public class FunctorDemo {
interface Fn1<A, B> {
B apply(A a);
}
interface Functor<A, F extends Functor<?, ?>> {
<B> F map(Fn1<A, B> f);
}
static class ObservableFunctor {
public static <A,B> Observable<B> map(Observable<A> fa, Fn1<A, B> f) {
return new Observable<B>(f.apply(fa.value));
}
}
static class Observable<A> {
public final A value;
public Observable(A _value) {
value = _value;
}
public String toString() {
return "Observable<" + value.getClass().getSimpleName() + ">(" + value.toString() + ")";
}
}
static class Event<A> implements Functor<A, Event<?>> {
public final A value;
public Event(A _value) {
value = _value;
}
public <B> Event<B> map(Fn1<A, B> f) {
return new Event<B>(f.apply(value));
}
public String toString() {
return "Event<" + value.getClass().getSimpleName() + ">(" + value.toString() + ")";
}
}
public static void main(String[] args) {
Observable<Event<Long>> oe1 = new Observable(new Event(42L));
System.out.println("oe1: " + oe1.toString()); // oe1: Observable<Event>(Event<Long>(42))
Observable<Event<String>> oe2 = ObservableFunctor.map(oe1,
new Fn1<Event<Long>, Event<String>>() {
public Event<String> apply(Event<Long> e) {
return e.map(
new Fn1<Long, String>() {
public String apply(Long l) {
return l.toString();
}
}
);
}
}
);
System.out.println("oe2: " + oe2.toString()); // oe2: Observable<Event>(Event<String>(42))
}
}