Skip to content

Instantly share code, notes, and snippets.

@umit
Created August 1, 2024 07:05
Show Gist options
  • Save umit/37d8499a102ccfa3c8b7fcca75462683 to your computer and use it in GitHub Desktop.
Save umit/37d8499a102ccfa3c8b7fcca75462683 to your computer and use it in GitHub Desktop.
Scala effects like java
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
class Scratch {
static String userName = "Morty";
static Effect<String> effect0 = saveUser(userName);
public static void main(String[] args) throws InterruptedException {
Thread.startVirtualThread(() -> {
Config.scenario = Optional.of(Scenario.HappyPath);
Effect<String> effect1 = effect0.retryN(2);
Effect<String> effect2 = effect1.orElseFail("FAILURE: User could not be saved");
Effect<String> effect3 = effect2.timeoutFail("** Save timed out **", Duration.ofSeconds(5));
Effect<String> effect4 = effect3.orElse(() -> sendToManualQueue(userName).run());
Effect<String> effect5 = effect4.withFinalizer(() -> logUserSignup(userName).run());
Effect<Pair<Duration, String>> effect6 = effect5.timed();
Effect<Optional<Pair<Duration, String>>> effect7 = effect6.when(!userName.equals("Morty"));
Effect<String> effect8 = new Effect<>(() -> {
System.out.println("Before save");
return effect1.run();
});
Effect<String> surroundedProgram = new Effect<>(() -> {
System.out.println("**Before**");
String result = effect8.debug().repeatN(1).run();
System.out.println("**After**");
return result;
});
surroundedProgram.run();
}).join();
}
enum Scenario {
HappyPath,
NeverWorks,
Slow,
WorksOnTry
}
public static class Config {
static Optional<Scenario> scenario;
Config(Optional<Scenario> scenario) {
this.scenario = scenario;
}
}
public static class Effect<T> {
private final Supplier<T> effect;
Effect(Supplier<T> effect) {
this.effect = effect;
}
T run() {
return effect.get();
}
Effect<T> retryN(int n) {
return new Effect<>(() -> {
for (int i = 0; i <= n; i++) {
try {
return effect.get();
} catch (Exception e) {
if (i == n) throw e;
}
}
throw new RuntimeException("Retry failed");
});
}
Effect<T> orElseFail(String errorMessage) {
return new Effect<>(() -> {
try {
return effect.get();
} catch (Exception e) {
throw new RuntimeException(errorMessage);
}
});
}
Effect<T> timeoutFail(String errorMessage, Duration timeout) {
return new Effect<>(() -> {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var future = executor.submit(effect::get);
return future.get(timeout.toMillis(), java.util.concurrent.TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new RuntimeException(errorMessage);
}
});
}
Effect<T> orElse(Supplier<T> alternative) {
return new Effect<>(() -> {
try {
return effect.get();
} catch (Exception e) {
return alternative.get();
}
});
}
Effect<T> withFinalizer(Runnable finalizer) {
return new Effect<>(() -> {
try {
return effect.get();
} finally {
finalizer.run();
}
});
}
Effect<Pair<Duration, T>> timed() {
return new Effect<>(() -> {
long start = System.nanoTime();
T result = effect.get();
long end = System.nanoTime();
return new Pair<>(Duration.ofNanos(end - start), result);
});
}
Effect<Optional<T>> when(boolean condition) {
return new Effect<>(() -> condition ? Optional.of(effect.get()) : Optional.empty());
}
Effect<T> debug() {
return new Effect<>(() -> {
T result = effect.get();
System.out.println(result);
return result;
});
}
Effect<T> repeatN(int n) {
return new Effect<>(() -> {
T result = null;
for (int i = 0; i <= n; i++) {
result = effect.get();
}
return result;
});
}
}
record Pair<A, B>(A first, B second) {
}
static Effect<String> saveUser(String username) {
return new Effect<>(() -> {
Scenario scenario = Config.scenario.orElse(Scenario.HappyPath);
return switch (scenario) {
case HappyPath -> "User saved";
case NeverWorks -> {
System.out.println("Log: **Database crashed!!**");
throw new RuntimeException("**Database crashed!!**");
}
case Slow -> {
try {
Thread.sleep(Duration.ofMinutes(1));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
yield "User saved";
}
case WorksOnTry -> {
AtomicInteger attempts = new AtomicInteger(0);
if (attempts.incrementAndGet() == 3) {
yield "User saved";
} else {
System.out.println("Log: **Database crashed!!**");
throw new RuntimeException("**Database crashed!!**");
}
}
};
});
}
static Effect<String> sendToManualQueue(String username) {
return new Effect<>(() -> "Please manually provision " + username);
}
static Effect<Void> logUserSignup(String username) {
return new Effect<>(() -> {
System.out.println("Log: Signup initiated for " + username);
return null;
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment