在Java 8中尝试monad

时间:2015-01-05 20:57:22

标签: java-8 monads

是否有针对monad的内置支持处理异常处理?与Scala Try类似的东西。我问,因为我不喜欢未经检查的例外。

6 个答案:

答案 0 :(得分:14)

至少有两个一般可用(例如在Maven Central上) - VavrCyclops都有Try实现,采用略有不同的方法。

Vavr's Try密切关注Scala的尝试。它将捕获所有非致命的'在执行它的组合器期间抛出的异常。

Cyclops Try只会捕获显式配置的异常(当然,默认情况下,它也可以捕获所有内容),默认的操作模式只是在初始填充方法中捕获。这背后的原因是,Try的行为方式与Optional相似 - Optional不会封装意外的Null值(即错误),只是我们合理预期没有值的地方。

以下是使用Cyclops资源试用的示例

 Try t2 = Try.catchExceptions(FileNotFoundException.class,IOException.class)
               .init(()->PowerTuples.tuple(new BufferedReader(new FileReader("file.txt")),new FileReader("hello")))
               .tryWithResources(this::read2);

另一个例子'提升'现有方法(可能除以零)以支持错误处理。

    import static org.hamcrest.Matchers.equalTo;
    import static org.junit.Assert.*;
    import static com.aol.cyclops.lambda.api.AsAnyM.anyM;
    import lombok.val;

    val divide = Monads.liftM2(this::divide);

    AnyM<Integer> result = divide.apply(anyM(Try.of(2, ArithmeticException.class)), anyM(Try.of(0)));

    assertThat(result.<Try<Integer,ArithmeticException>>unwrapMonad().isFailure(),equalTo(true));
 private Integer divide(Integer a, Integer b){
    return a/b;
 }

答案 1 :(得分:13)

GitHub上的“better-java-monads”项目有一个针对Java 8的Try monad here

答案 2 :(得分:4)

首先,让我为回答而不是评论而道歉 - 显然我需要50个声誉才能发表评论......

@ncaralicea你的实现类似于我自己,但我遇到的问题是如何调整bind()中的try ... catch与身份定律。具体来说, return x&gt;&gt; = f相当于f x 。当bind()捕获异常时, f x 会因为它抛出而不同。

此外,ITransformer似乎是 a - &gt; b 而不是 a - &gt; M b 。我当前的bind()版本,虽然我发现它不满意,但是

public <R> MException<R> bind(final Function<T, MException<R>> f) {
    Validate.notNull(f);
    if (value.isRight())
        try {
            return f.apply(value.right().get());
        } catch (final Exception ex) {
            return new MException<>(Either.<Exception, R>left(ex));
        }
    else
        return new MException<>(Either.<Exception, R>left(value.left().get()));
}

其中value是

Either<? extends Exception,T>

身份法的问题在于它需要函数f来捕获异常,从而破坏了练习的整个目的。

我认为你真正想要的是Functor而不是Monad。那就是fmap:(a-&gt; b) - &gt; f a - &gt; f b功能。

如果你写

@Override
public <R> MException<R> fmap(final Function<T, R> fn) {
    Validate.notNull(fn);
    if (value.isRight())
        try {
            return new MException<>(Either.<Exception, R>right(fn.apply(value.right().get())));
        } catch (final Exception ex) {
            return new MException<>(Either.<Exception, R>left(ex));
        }
    else
        return new MException<>(Either.<Exception, R>left(value.left().get()));
}

然后你不需要编写显式异常处理代码,实现新接口或搞乱Monad法则。

答案 3 :(得分:3)

你可以使用CompletableFuture通过(ab)做你想做的事。请不要在任何生产代码中执行此操作。

CompletableFuture<Scanner> sc = CompletableFuture.completedFuture(
                                                      new Scanner(System.in));

CompletableFuture<Integer> divident = sc.thenApply(Scanner::nextInt);
CompletableFuture<Integer> divisor = sc.thenApply(Scanner::nextInt);

CompletableFuture<Integer> result = divident.thenCombine(divisor, (a,b) -> a/b);

result.whenComplete((val, ex) -> {
    if (ex == null) {
        System.out.printf("%s/%s = %s%n", divident.join(), divisor.join(), val);
    } else {
        System.out.println("Something went wrong");
    }
});

答案 4 :(得分:1)

这里有一个可以用作模型的实现。 更多信息可以在这里找到:

Java with Try, Failure, and Success based computations

你基本上可以这样做:

public class Test {

  public static void main(String[] args) {

    ITransformer < String > t0 = new ITransformer < String > () {@
      Override
      public String transform(String t) {
        //return t + t;
        throw new RuntimeException("some exception 1");
      }
    };

    ITransformer < String > t1 = new ITransformer < String > () {@
      Override
      public String transform(String t) {
        return "<" + t + ">";
        //throw new RuntimeException("some exception 2");
      }
    };

    ComputationlResult < String > res = ComputationalTry.initComputation("1").bind(t0).bind(t1).getResult();

    System.out.println(res);

    if (res.isSuccess()) {
      System.out.println(res.getResult());
    } else {
      System.out.println(res.getError());
    }
  }
}

以下是代码:

public class ComputationalTry < T > {

  final private ComputationlResult < T > result;

  static public < P > ComputationalTry < P > initComputation(P argument) {
    return new ComputationalTry < P > (argument);
  }

  private ComputationalTry(T param) {
    this.result = new ComputationalSuccess < T > (param);
  }

  private ComputationalTry(ComputationlResult < T > result) {
    this.result = result;
  }

  private ComputationlResult < T > applyTransformer(T t, ITransformer < T > transformer) {
    try {
      return new ComputationalSuccess < T > (transformer.transform(t));
    } catch (Exception throwable) {
      return new ComputationalFailure < T, Exception > (throwable);
    }
  }

  public ComputationalTry < T > bind(ITransformer < T > transformer) {
    if (result.isSuccess()) {
      ComputationlResult < T > resultAfterTransf = this.applyTransformer(result.getResult(), transformer);
      return new ComputationalTry < T > (resultAfterTransf);
    } else {
      return new ComputationalTry < T > (result);
    }
  }

  public ComputationlResult < T > getResult() {
    return this.result;
  }
}


public class ComputationalFailure < T, E extends Throwable > implements ComputationlResult < T > {

  public ComputationalFailure(E exception) {
    this.exception = exception;
  }

  final private E exception;

  @Override
  public T getResult() {
    return null;
  }

  @Override
  public E getError() {
    return exception;
  }

  @Override
  public boolean isSuccess() {
    return false;
  }

}


public class ComputationalSuccess < T > implements ComputationlResult < T > {

  public ComputationalSuccess(T result) {
    this.result = result;
  }

  final private T result;

  @Override
  public T getResult() {
    return result;
  }

  @Override
  public Throwable getError() {
    return null;
  }

  @Override
  public boolean isSuccess() {
    return true;
  }
}


public interface ComputationlResult < T > {

  T getResult();

  < E extends Throwable > E getError();

  boolean isSuccess();

}


public interface ITransformer < T > {

  public T transform(T t);

}


public class Test {

  public static void main(String[] args) {

    ITransformer < String > t0 = new ITransformer < String > () {@
      Override
      public String transform(String t) {
        //return t + t;
        throw new RuntimeException("some exception 1");
      }
    };

    ITransformer < String > t1 = new ITransformer < String > () {@
      Override
      public String transform(String t) {
        return "<" + t + ">";
        //throw new RuntimeException("some exception 2");
      }
    };

    ComputationlResult < String > res = ComputationalTry.initComputation("1").bind(t0).bind(t1).getResult();

    System.out.println(res);

    if (res.isSuccess()) {
      System.out.println(res.getResult());
    } else {
      System.out.println(res.getError());
    }
  }
}

我希望这可能会掩盖一些亮点。

答案 5 :(得分:1)

@Misha正在做点什么。显然你不会在实际代码中做到这一点,但是<body> @content <script src="@routes.Assets.at("javascripts/jquery.min.js")"></script> <script src="@routes.Assets.at("bootstrap/js/bootstrap.min.js")"></script> </body> 提供了像这样的Haskell风格的monad:

  • <div id="c1" class="carousel slide" data-ride="carousel"> <div class="carousel-inner"> <div class="item active"> <div class="row"> <div class="col-xs-offset-2 col-xs-8"> <p class="lead"> <i>sample item</i> </p> <p class="lead text-right"> <i>sample, Inc.</i> </p> </div> </div> </div> <div class="item"> <div class="row"> <div class="col-xs-offset-2 col-xs-8"> <p class="lead"> <i>sample2</i> </p> <p class="lead text-right"> <i>AmericanInitiative, Inc.</i> </p> </div> </div> </div> </div> <!-- carousel button --> <a class="carousel-control left carousel-black" href="#c1" data-slide="prev"> <span class="glyphicon glyphicon-chevron-left"></span> </a> <a class="carousel-control right carousel-black" href="#c1" data-slide="next"> <span class="glyphicon glyphicon-chevron-right"></span> </a> </div> 映射到CompletableFuture
  • return映射到CompletableFuture.completedFuture

所以你可以像这样重写@Misha的例子:

>=

映射到Haskell-ish:

thenCompose

或使用do语法

CompletableFuture.completedFuture(new Scanner(System.in)).thenCompose(scanner ->
CompletableFuture.completedFuture(scanner.nextInt()).thenCompose(divident ->
CompletableFuture.completedFuture(scanner.nextInt()).thenCompose(divisor ->
CompletableFuture.completedFuture(divident / divisor).thenCompose(val -> {
   System.out.printf("%s/%s = %s%n", divident, divisor, val);
   return null;
}))));

(return (newScanner SystemIn)) >>= \scanner -> (return (nextInt scanner)) >>= \divident -> (return (nextInt scanner)) >>= \divisor -> (return (divident / divisor)) >>= \val -> do SystemOutPrintf "%s/%s = %s%n" divident divisor val return Null do scanner <- return (newScanner SystemIn) divident <- return (nextInt scanner) divisor <- return (nextInt scanner) val <- return (divident / divisor) do SystemOutPrintf "%s/%s = %s%n" divident divisor val return Null

的实现

我有点被带走了。这些是根据fmap

实施的标准joinfmap
join