Created
August 1, 2024 07:05
-
-
Save umit/37d8499a102ccfa3c8b7fcca75462683 to your computer and use it in GitHub Desktop.
Scala effects like java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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