是否有针对monad的内置支持处理异常处理?与Scala Try类似的东西。我问,因为我不喜欢未经检查的例外。
答案 0 :(得分:14)
至少有两个一般可用(例如在Maven Central上) - Vavr和Cyclops都有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
:
join
和fmap
join