Skip to content

Instantly share code, notes, and snippets.

@tonivade
Last active May 14, 2019 13:06
Show Gist options
  • Save tonivade/60a0892ca76979cd4c8b4fb55898890c to your computer and use it in GitHub Desktop.
Save tonivade/60a0892ca76979cd4c8b4fb55898890c to your computer and use it in GitHub Desktop.
Proof of concept of enviromental effects in java
/*
* Copyright (c) 2018-2019, Antonio Gabriel Muñoz Conejo <antoniogmc at gmail dot com>
* Distributed under the terms of the MIT License
*/
package com.github.tonivade.purefun.zio;
import static com.github.tonivade.purefun.monad.Console.println;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import org.junit.jupiter.api.Test;
import com.github.tonivade.purefun.Nothing;
public class EnvEffectsTest {
@Test
public void program() {
ConsoleExecutor executor = new ConsoleExecutor().read("Toni");
executor.run(() -> echo().provide(Console.live()));
assertEquals("what's your name?\nHello Toni\n", executor.getOutput());
}
private ZIO<Console, Throwable, Nothing> echo() {
return println("what's your name?")
.andThen(Console::readln)
.flatMap(name -> println("Hello " + name));
}
}
interface Console {
<R extends Console> Console.Service<R> console();
static ZIO<Console, Throwable, String> readln() {
return ZIO.accessM(env -> env.console().readln());
}
static ZIO<Console, Throwable, Nothing> println(String text) {
return ZIO.accessM(env -> env.console().println(text));
}
interface Service<R extends Console> {
ZIO<R, Throwable, String> readln();
ZIO<R, Throwable, Nothing> println(String text);
}
static Console live() {
return new Console() {
@Override
public <R extends Console> Service<R> console() {
return new Console.Service<R>() {
@Override
public ZIO<R, Throwable, String> readln() {
return ZIO.from(() -> reader().readLine());
}
@Override
public ZIO<R, Throwable, Nothing> println(String text) {
return ZIO.exec(() -> writer().println(text));
}
private BufferedReader reader() {
return new BufferedReader(new InputStreamReader(System.in));
}
private PrintWriter writer() {
return new PrintWriter(System.out, true);
}
};
}
};
}
}
/*
* Copyright (c) 2018-2019, Antonio Gabriel Muñoz Conejo <antoniogmc at gmail dot com>
* Distributed under the terms of the MIT License
*/
package com.github.tonivade.purefun.zio;
import static com.github.tonivade.purefun.Function1.cons;
import static com.github.tonivade.purefun.Function1.identity;
import static com.github.tonivade.purefun.Nothing.nothing;
import static com.github.tonivade.purefun.zio.ZIOModule.flatMapValue;
import static com.github.tonivade.purefun.zio.ZIOModule.mapValue;
import com.github.tonivade.purefun.CheckedFunction1;
import com.github.tonivade.purefun.CheckedProducer;
import com.github.tonivade.purefun.CheckedRunnable;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Function2;
import com.github.tonivade.purefun.Nothing;
import com.github.tonivade.purefun.Producer;
import com.github.tonivade.purefun.type.Either;
import com.github.tonivade.purefun.type.Try;
@FunctionalInterface
public interface ZIO<R, E, A> {
ZIO<?, ?, Nothing> UNIT = pure(nothing());
Either<E, A> provide(R env);
default <B> ZIO<R, E, B> map(Function1<A, B> map) {
return mapValue(this, value -> value.map(map));
}
default <B> ZIO<R, E, B> flatMap(Function1<A, ZIO<R, E, B>> map) {
return flatMapValue(this, value -> value.map(map).fold(ZIO::raiseError, identity()));
}
@SuppressWarnings("unchecked")
default <B> ZIO<R, E, B> flatten() {
try {
return ((ZIO<R, E, ZIO<R, E, B>>) this).flatMap(identity());
} catch (ClassCastException e) {
throw new UnsupportedOperationException("cannot be flattened");
}
}
default ZIO<R, A, E> swap() {
return mapValue(this, Either<E, A>::swap);
}
default <B> ZIO<R, B, A> mapError(Function1<E, B> map) {
return mapValue(this, value -> value.mapLeft(map));
}
default <F> ZIO<R, F, A> flatMapError(Function1<E, ZIO<R, F, A>> map) {
return flatMapValue(this, value -> value.mapLeft(map).fold(identity(), ZIO::pure));
}
default <B, F> ZIO<R, F, B> bimap(Function1<E, F> mapError, Function1<A, B> map) {
return mapValue(this, value -> value.bimap(mapError, map));
}
default <B> ZIO<R, E, B> andThen(Producer<ZIO<R, E, B>> next) {
return flatMap(ignore -> next.get());
}
default <B, F> ZIO<R, F, B> foldM(Function1<E, ZIO<R, F, B>> mapError, Function1<A, ZIO<R, F, B>> map) {
return env -> provide(env).fold(mapError, map).provide(env);
}
default <B> ZIO<R, Nothing, B> fold(Function1<E, B> mapError, Function1<A, B> map) {
return foldM(mapError.andThen(ZIO::pure), map.andThen(ZIO::pure));
}
default ZIO<R, E, A> orElse(Producer<ZIO<R, E, A>> other) {
return foldM(other.asFunction(), cons(this));
}
static <R, E, A> ZIO<R, E, A> accessM(Function1<R, ZIO<R, E, A>> map) {
return env -> map.apply(env).provide(env);
}
static <R, A> ZIO<R, Nothing, A> access(Function1<R, A> map) {
return accessM(map.andThen(ZIO::pure));
}
static <R> ZIO<R, Nothing, R> env() {
return access(identity());
}
static <R, E, A, B, C> ZIO<R, E, C> map2(ZIO<R, E, A> za, ZIO<R, E, B> zb, Function2<A, B, C> mapper) {
return za.flatMap(a -> zb.map(b -> mapper.curried().apply(a).apply(b)));
}
static <R, E, A> ZIO<R, E, A> absorb(ZIO<R, E, Either<E, A>> value) {
return mapValue(value, Either::flatten);
}
static <R, A, B> Function1<A, ZIO<R, Throwable, B>> lift(CheckedFunction1<A, B> function) {
return value -> from(() -> function.apply(value));
}
static <R, E, A> ZIO<R, E, A> from(Producer<Either<E, A>> task) {
return env -> task.get();
}
static <R, A> ZIO<R, Throwable, A> from(CheckedProducer<A> task) {
return env -> Try.of(task).toEither();
}
static <R> ZIO<R, Throwable, Nothing> exec(CheckedRunnable task) {
return from(task.asProducer());
}
static <R, E, A> ZIO<R, E, A> pure(A value) {
return env -> Either.right(value);
}
static <R, E, A> ZIO<R, E, A> raiseError(E error) {
return env -> Either.left(error);
}
@SuppressWarnings("unchecked")
static <R, E> ZIO<R, E, Nothing> unit() {
return (ZIO<R, E, Nothing>) UNIT;
}
}
interface ZIOModule {
static <R, E, F, A, B> ZIO<R, F, B> mapValue(ZIO<R, E, A> self, Function1<Either<E, A>, Either<F, B>> map) {
return env -> map.apply(self.provide(env));
}
static <R, E, F, A, B> ZIO<R, F, B> flatMapValue(ZIO<R, E, A> self, Function1<Either<E, A>, ZIO<R, F, B>> map) {
return env -> map.apply(self.provide(env)).provide(env);
}
}
@tonivade
Copy link
Author

tonivade commented Mar 23, 2019

more details at purefun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment