是否有可能在Java中构造一个允许flatMap返回不同的左值的或?
我试图理解这两种方法是如何实现的。我在将多个函数链接在一起时遇到了困难,这种链接允许在是否有可能在Java中构造一个允许flatMap返回不同的左值的或?,java,functional-programming,monads,Java,Functional Programming,Monads,我试图理解这两种方法是如何实现的。我在将多个函数链接在一起时遇到了困难,这种链接允许在flatMap期间返回不同的Left值。我无法计算出在类型系统中它是如何可能的 最小示例代码: public class Either<A,B> { public final A left; public final B right; private Either(A a, B b) { left = a; right = b; }
flatMap
期间返回不同的Left
值。我无法计算出在类型系统中它是如何可能的
最小示例代码:
public class Either<A,B> {
public final A left;
public final B right;
private Either(A a, B b) {
left = a;
right = b;
}
public static <A, B> Either<A, B> left(A a) {
return new Either<>(a, null);
}
public static <A, B> Either<A, B> right(B b) {
return new Either<>(null, b);
}
public <C> Either<A, C> flatMap(Function<B, Either<A,C>> f) {
if (this.isRight()) return f.apply(this.right);
else return Either.left(this.left);
}
// map and other useful functions....
但是,编译器将此标记为不可能
在研究了一些代码之后,我意识到这是由于我的
通用参数造成的
flatMap有两种不同的情况:
不起作用,因为如果执行案例1并且我的函数更改A
,那么案例2无效,因为A
!=<代码>A'。将函数应用到右侧的行为可能已将左侧更改为不同的类型
所有这些让我想到了这些问题:
Left
类型李>
由于参数性,没有您想要的合理的
flatMap()
函数。考虑:
Either<Foo, String> e1 = Either.left(new Foo());
Either<Bar, String> e2 = foo.flatMap(x -> doThing2());
Bar bar = e2.left; // Where did this come from???
可以,但是旧的
Left
必须是新的Left
的子类型或与之相等,因此可以将其强制转换。我不太熟悉Java的语法,但Scala实现看起来像:
def flatMap[A1>:A,B1](f:B=>任一[A1,B1]):任一[A1,B1]=此匹配{
案例右侧(b)=>f(b)
case=>this.asInstanceOf[A1,B1]]
}
这里的
A1>:A
将A
指定为A1
的子类型。我知道Java有一个
语法,但我不确定它是否可以用来描述A1
上的约束,就像我们在本例中需要的那样。关于或者的用法(点(…)
),您的平面映射似乎不够。我假设您希望平面映射像可选的那样工作
Optional.flatMap
的映射器获取一个T
类型的值,并返回一个Optional
,其中U
是此方法的通用类型参数。但是Optional
有一个泛型类型参数T
,而或有两个:A
和B
。因此,如果您想要平面映射或,则仅使用一个映射是不够的
一个映射它应该映射什么?“不是空值的值”你会说-不是吗?好的,但您首先在运行时知道这一点。您的flatMap
方法是在编译时定义的。因此,您必须为每种情况提供映射
您可以选择平面图(函数f)
。此映射使用类型为B
的值作为输入。这意味着如果映射的或为!eather.isRight()
以下所有映射都将返回一个eather.left(a)
,其中a
是第一个eather.left(a)
的值。因此实际上只有other
其中other.isRight()
可以映射到另一个值。而且从一开始就必须是或.isRight()
。这也意味着,一旦创建了other
,所有平面映射都将产生一种other
。因此当前的flatMap
限制或以保持其左泛型类型。这就是你应该做的吗
如果要无限制地平面映射任一
,则这两种情况都需要映射:任一.isRight()
和!.isRight()之一
。这将允许您在两个方向上继续展开贴图
我是这样做的:
public class Either<A, B> {
public final A left;
public final B right;
private Either(A a, B b) {
left = a;
right = b;
}
public boolean isRight() {
return right != null;
}
@Override
public String toString() {
return isRight() ?
right.toString() :
left.toString();
}
public static <A, B> Either<A, B> left(A a) {
return new Either<>(a, null);
}
public static <A, B> Either<A, B> right(B b) {
return new Either<>(null, b);
}
public <C, D> Either<C, D> flatMap(Function<A, Either<C, D>> toLeft, Function<B, Either<C, D>> toRight) {
if (this.isRight()) {
return toRight.apply(this.right);
} else {
return toLeft.apply(this.left);
}
}
public static void main(String[] args) {
Either<String, String> left = Either.left(new Foo("left"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"));
System.out.println(left); // left.right.right
Either<String, String> right = Either.right(new Foo("right"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"))
.flatMap(l -> Either.right(l.toString() + ".right"), r -> Either.left(r.toString() + ".left"));
System.out.println(right); // right.left.left.right
}
private static class Foo {
private String s;
public Foo(String s) {
this.s = s;
}
@Override
public String toString() {
return s;
}
}
private static class Bar {
private String s;
public Bar(String s) {
this.s = s;
}
@Override
public String toString() {
return s;
}
}
private static class Baz {
private String s;
public Baz(String s) {
this.s = s;
}
@Override
public String toString() {
return s;
}
}
}
公共类{
公开决赛A左;
公共最终B权;
二等兵(甲、乙){
左=a;
右=b;
}
公共布尔值isRight(){
返回右侧!=null;
}
@凌驾
公共字符串toString(){
返回isRight()?
对。toString()
左。toString();
}
公共静态或左(A){
返回新的(a,null);
}
公共静态任意一项权利(B){
返回新的(null,b);
}
公共平面地图(左功能,右功能){
if(this.isRight()){
返回右侧。应用(此右侧);
}否则{
返回到左。应用(此。左);
}
}
公共静态void main(字符串[]args){
要么左=要么左(新的Foo(“左”))
.flatMap(l->eather.right(新条(l.toString()+“.right”)),r->eather.left(新Baz(r.toString()+“.left”))
.flatMap(l->eather.left(l.toString()+“.left”)、r->eather.right(r.toString()+”.right”);
System.out.println(左);//左.右.右
任意右=任意。右(新的Foo(“右”))
.flatMap(l->eather.right(新条(l.toString()+“.right”)),r->eather.left(新Baz(r.toString()+“.left”))
.flatMap(l->eather.left(l.toString()+“.left”)、r->eather.right(r.toString()+“.right”))
.flatMap(l->eather.right(l.toString()+“.right”)、r->eather.left(r.toString()+“.left”);
System.out.println(右);//right.left.left.right
}
私有静态类Foo{
私有字符串;
公共Foo(字符串s){
这个.s=s;
}
@凌驾
公共字符串toString(){
返回s;
}
}
专用静态类栏{
私有字符串;
公共酒吧{
Either<Foo, String> e1 = Either.left(new Foo());
Either<Bar, String> e2 = foo.flatMap(x -> doThing2());
Bar bar = e2.left; // Where did this come from???
public <C, D> Either<C, D> flatMap(Function<B, Either<C, D>> f) {
if (this.isRight()) {
return f.apply(this.right);
} else {
// Error: can't convert A to C
return Either.left(this.left);
}
}
public class Either<A, B> {
public final A left;
public final B right;
private Either(A a, B b) {
left = a;
right = b;
}
public boolean isRight() {
return right != null;
}
@Override
public String toString() {
return isRight() ?
right.toString() :
left.toString();
}
public static <A, B> Either<A, B> left(A a) {
return new Either<>(a, null);
}
public static <A, B> Either<A, B> right(B b) {
return new Either<>(null, b);
}
public <C, D> Either<C, D> flatMap(Function<A, Either<C, D>> toLeft, Function<B, Either<C, D>> toRight) {
if (this.isRight()) {
return toRight.apply(this.right);
} else {
return toLeft.apply(this.left);
}
}
public static void main(String[] args) {
Either<String, String> left = Either.left(new Foo("left"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"));
System.out.println(left); // left.right.right
Either<String, String> right = Either.right(new Foo("right"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"))
.flatMap(l -> Either.right(l.toString() + ".right"), r -> Either.left(r.toString() + ".left"));
System.out.println(right); // right.left.left.right
}
private static class Foo {
private String s;
public Foo(String s) {
this.s = s;
}
@Override
public String toString() {
return s;
}
}
private static class Bar {
private String s;
public Bar(String s) {
this.s = s;
}
@Override
public String toString() {
return s;
}
}
private static class Baz {
private String s;
public Baz(String s) {
this.s = s;
}
@Override
public String toString() {
return s;
}
}
}