Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes bug #1216 #1217

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions javaslang/generator/Generator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,41 @@ def generateMainClasses(): Unit = {
//

/**
* Runs a {@code unit} of work and returns {@code Void}. This is helpful when a return value is expected,
* e.g. by {@code Match}:
* Lifts the {@link Runnable} to a value by returning the given {@code unit} of work.
* The given {@code Runnable} is not evaluated.
* <p>
* <strong>Motivation:</strong>
* <p>
* Lifting a {@code Runnable} is helpful in situations where an argument may be a value or a functional
* interface:
*
* <pre><code>R m(T t);
* R m(Function&lt;T, U&gt; f)</code></pre>
*
* Here Java does not allow to call
*
* <pre><code>// Error: m(T) cannot be applied to m(&lt;lambda expression&gt;)
* m(() -&gt; {});</code></pre>
*
* Solution: we lift the {@code Runnable}:
*
* <pre><code>m(run(() -&gt; {}));</code></pre>
*
* Please note that {@code run()} does not evaluate the given Runnable.
*
* <strong>Example:</strong>
*
* <pre><code>Match(i).of(
* Case(0, run(() -&gt; System.out.println("zero"))),
* Case(1, run(() -&gt; System.out.println("one"))),
* Case($$(), run(() -&gt; System.out.println("many")))
* )</code></pre>
* ).run();</code></pre>
*
* @param unit A block of code to be run.
* @return the single instance of {@code Void}, namely {@code null}
* @param unit A block of code represented by a {@code Runnable} lambda or a method reference.
* @return the given {@code unit}
*/
public static Void run(Runnable unit) {
unit.run();
return null;
public static Runnable run(Runnable unit) {
return unit;
}
"""
}
Expand Down Expand Up @@ -1187,10 +1207,9 @@ def generateTestClasses(): Unit = {
// -- run

@$test
public void shouldRunUnitAndReturnVoid() {
public void shouldRunUnit() {
int[] i = { 0 };
@SuppressWarnings("unused")
Void nothing = run(() -> i[0]++);
run(() -> i[0]++).run();
$assertThat(i[0]).isEqualTo(1);
}

Expand Down
36 changes: 28 additions & 8 deletions javaslang/src-gen/main/java/javaslang/API.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,41 @@ private API() {
//

/**
* Runs a {@code unit} of work and returns {@code Void}. This is helpful when a return value is expected,
* e.g. by {@code Match}:
* Lifts the {@link Runnable} to a value by returning the given {@code unit} of work.
* The given {@code Runnable} is not evaluated.
* <p>
* <strong>Motivation:</strong>
* <p>
* Lifting a {@code Runnable} is helpful in situations where an argument may be a value or a functional
* interface:
*
* <pre><code>R m(T t);
* R m(Function&lt;T, U&gt; f)</code></pre>
*
* Here Java does not allow to call
*
* <pre><code>// Error: m(T) cannot be applied to m(&lt;lambda expression&gt;)
* m(() -&gt; {});</code></pre>
*
* Solution: we lift the {@code Runnable}:
*
* <pre><code>m(run(() -&gt; {}));</code></pre>
*
* Please note that {@code run()} does not evaluate the given Runnable.
*
* <strong>Example:</strong>
*
* <pre><code>Match(i).of(
* Case(0, run(() -&gt; System.out.println("zero"))),
* Case(1, run(() -&gt; System.out.println("one"))),
* Case($(), run(() -&gt; System.out.println("many")))
* )</code></pre>
* ).run();</code></pre>
*
* @param unit A block of code to be run.
* @return the single instance of {@code Void}, namely {@code null}
* @param unit A block of code represented by a {@code Runnable} lambda or a method reference.
* @return the given {@code unit}
*/
public static Void run(Runnable unit) {
unit.run();
return null;
public static Runnable run(Runnable unit) {
return unit;
}

//
Expand Down
5 changes: 2 additions & 3 deletions javaslang/src-gen/test/java/javaslang/APITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@ public void shouldNotBeInstantiable() {
// -- run

@Test
public void shouldRunUnitAndReturnVoid() {
public void shouldRunUnit() {
int[] i = { 0 };
@SuppressWarnings("unused")
Void nothing = run(() -> i[0]++);
run(() -> i[0]++).run();
assertThat(i[0]).isEqualTo(1);
}

Expand Down
62 changes: 62 additions & 0 deletions javaslang/src/test/java/javaslang/MatchTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@

import java.math.BigDecimal;
import java.time.Year;
import java.util.Objects;

import static javaslang.API.$;
import static javaslang.API.*;
import static javaslang.MatchTest_DeveloperPatterns.Developer;
import static javaslang.Patterns.*;
import static javaslang.Predicates.instanceOf;
import static javaslang.Predicates.is;
import static javaslang.Predicates.isIn;
import static org.assertj.core.api.Assertions.assertThat;

public class MatchTest {
Expand Down Expand Up @@ -256,6 +258,66 @@ public void shouldDecomposeListWithNonEmptyTail() {
assertThat(actual).isEqualTo("Some(1)::List(Some(2.0))");
}

// -- run

@Test
public void shouldRunUnitOfWork() {

class OuterWorld {

String effect = null;

void displayHelp() {
effect = "help";
}

void displayVersion() {
effect = "version";
}
}

final OuterWorld outerWorld = new OuterWorld();

Match("-v").of(
Case(isIn("-h", "--help"), run(outerWorld::displayHelp)),
Case(isIn("-v", "--version"), run(outerWorld::displayVersion)),
Case($(), run(() -> {
throw new IllegalArgumentException();
}))
).run();

assertThat(outerWorld.effect).isEqualTo("version");
}

@Test
public void shouldRunWithInferredArguments() {

class OuterWorld {

Number effect = null;

void writeInt(int i) {
effect = i;
}

void writeDouble(double d) {
effect = d;
}

}

final OuterWorld outerWorld = new OuterWorld();
final Object obj = .1d;

Match(obj).of(
Case(instanceOf(Integer.class), i -> run(() -> outerWorld.writeInt(i))),
Case(instanceOf(Double.class), d -> run(() -> outerWorld.writeDouble(d))),
Case($(), run(() -> { throw new NumberFormatException(); }))
).run();

assertThat(outerWorld.effect).isEqualTo(.1d);
}

// -- Developer

@Test
Expand Down