Giter VIP home page Giter VIP logo

purefun's Introduction

Purefun

 ____                  __             
|  _ \ _   _ _ __ ___ / _|_   _ _ __  
| |_) | | | | '__/ _ \ |_| | | | '_ \ 
|  __/| |_| | | |  __/  _| |_| | | | |
|_|    \__,_|_|  \___|_|  \__,_|_| |_|

Build Status Codacy Badge Join the chat at https://gitter.im/tonivade/purefun

This module was developed as the core of the zeromock project. It defines all the basic classes and interfaces used in the rest of the project.

Initially the module only held a few basic interfaces and it has grown to become an entire functional programming library (well, a humble one), and now is an independent library.

Working in this library helped me to learn and understand some important concepts of functional programming, and over time I implemented higher kinded types and type classes in Java. I don't know if this will be helpful for somebody, but the work is here to everyone want to use it.

Finally, I have to say thanks to vavr library author, this library is largely inspired in his work, and also to Scala standard library authors. I don't want to forget some of the projects I've used as reference: Arrow and cats for type classes implementation, fs2 for stream processing, and ZIO to implement my own version in Java. Their awesome work help me a lot.

Disclaimer

This project is not ready to be used in production, I use it to learn functional programming concepts by my self, but, if you want to use it, use it at your own risk. Anyway if you think is useful for you, go ahead, also any feedback and PR are very welcome.

Higher Kinded Types

In this project I have implemented some patterns of functional programming that need Higher Kinded Types. In Java there are not such thing, but it can be simulated using a especial codification of types.

In Scala we can define a higher kinded typed just like this Monad[F[_]] but in Java it can be codified like this Monad<F>. Then we can define a type using a special codification like this:

interface SomeType<T> extends SomeTypeOf<T> { }

// Boilerplate
interface SomeTypeOf<T> implements Kind<SomeType<?>, T> {

  // this is a safe cast
  static SomeType<T> toSomeType(Kind<SomeType<?>, ? extends T> hkt) {
    return (SomeType<T>) hkt;
  }
}

It can be triky but, in the end is easy to work with. By the way, I tried to hide this details to the user of the library. Except with type classes because is the only way to implement them correctly.

So, there are interfaces to encode kinds of 1, 2 and 3 types. It can be defined types for 4, 5 or more types, but it wasn't necessary to implement the library.

Annotation Processor

In order to simplify working with higher kinded types, in the last version I've included an annotation processor to generate all this boilerplate code:

@HigherKind
interface SomeType<T> extends SomeTypeOf<T> { }

With this annotation, all the above code, is generated automatically.

Data types

Option

Is an alternative to Optional of Java standard library. It can contains two values, a some or a none

Option<String> some = Option.some("Hello world");

Option<String> none = Option.none();

Try

Is an implementation of scala Try in Java. It can contains two values, a success or a failure.

Try<String> success = Try.success("Hello world");

Try<String> failure = Try.failure(new RuntimeException("Error"));

Either

Is an implementation of scala Either in Java.

Either<Integer, String> right = Either.right("Hello world");

Either<Integer, String> left = Either.left(100);

Validation

This type represents two different states, valid or invalid, an also it allows to combine several validations using map2 to map5 methods.

Validation<String, String> name = Validation.valid("John Smith");
Validation<String, String> email = Validation.valid("[email protected]");

// Person has a constructor with two String parameters, name and email.
Valdation<Sequence<String>, Person> person = Validation.map2(name, email, Person::new);

Const

A object with a phantom parameter:

Const<String, Integer> constInt = Const.of("Hello world!");

Const<String, Float> constFloat = constInt.retag();

assertEquals("Hello world!", constFloat.value());

Future

This is an experimental implementation of Future. Computations are executed in another thread inmediatelly.

Future<String> future = Future.success("Hello world!");

Future<String> result = future.flatMap(string -> Future.run(string::toUpperCase));

assertEquals(Try.success("HELLO WORLD!"), result.await());

Tuples

These classes allow to hold some values together, as tuples. There are tuples from 1 to 5.

Tuple1<String> tuple1 = Tuple.of("Hello world");

Tuple2<String, Integer> tuple2 = Tuple.of("John Smith", 100);

Data structures

Java doesn't define immutable collections, so I have implemented some of them.

Sequence

Is the equivalent to java Collection interface. It defines all the common methods.

ImmutableList

It represents a linked list. It has a head and a tail.

ImmutableSet

It represents a set of elements. This elements cannot be duplicated.

ImmutableArray

It represents an array. You can access to the elements by its position in the array.

ImmutableMap

This class represents a hash map.

ImmutableTree

This class represents a binary tree.

ImmutableTreeMap

This class represents a binary tree map.

Monads

Also I have implemented some Monads that allows to combine some operations.

State Monad

Is the traditional State Modad from FP languages, like Haskel or Scala. It allows to combine operations over a state. The state should be a immutable class. It recives an state and generates a tuple with the new state and an intermediate result.

State<ImmutableList<String>, Option<String>> read = State.state(list -> Tuple.of(list.tail(), list.head()));

Tuple<ImmutableList<String>, Option<String>> result = read.run(ImmutableList.of("a", "b", "c"));

assertEquals(Tuple.of(ImmutableList.of("b", "c"), Option.some("a")), result);

Reader Monad

This is an implementation of Reader Monad. It allows to combine operations over a common input. It can be used to inject dependencies.

Reader<ImmutableList<String>, String> read2 = Reader.reader(list -> list.tail().head().orElse(""));

String result = read2.eval(ImmutableList.of("a", "b", "c"));

assertEqual("b", result);

Writer Monad

It allow to combine operations over a common output.

Writer<ImmutableList<String>, Integer> writer = Writer.<String, Integer>listPure(5)
    .flatMap(value -> listWriter("add 5", value + 5))
    .flatMap(value -> listWriter("plus 2", value * 2));

assertAll(() -> assertEquals(Integer.valueOf(20), writer.getValue()),
          () -> assertEquals(listOf("add 5", "plus 2"), writer.getLog()));

IO Monad

This is a experimental implementation of IO Monad in java. Inspired in this work.

IO<Unit> echo = Console.print("write your name")
  .andThen(Console.read())
  .flatMap(name -> Console.print("Hello " + name))
  .andThen(Console.print("end"));

echo.unsafeRunSync();

Trampoline

Implements recursion using an iteration and is stack safe.

private Trampoline<Integer> fibLoop(Integer n) {
if (n < 2) {
  return Trampoline.done(n);
}
return Trampoline.more(() -> fibLoop(n - 1)).flatMap(x -> fibLoop(n - 2).map(y -> x + y));
}

Free

Free Monad

Finally, after hours of hard coding, I managed to implement a Free monad. This is a highly unstable implementation and I have implemented because it can be implemented. Inspired in this work.

Free<IOProgram_, Unit> echo =
  IOProgram.write("what's your name?")
    .andThen(IOProgram.read())
    .flatMap(text -> IOProgram.write("Hello " + text))
    .andThen(IOProgram.write("end"));

Kind<IO_, Unit> foldMap = echo.foldMap(IOInstances.monad(), new IOProgramInterperter());

foldMap.fix(toIO()).unsafeRunSync();

Free Applicative

Similar to Free monad, but allows static analysis without to run the program.

FreeAp<DSL_, Tuple5<Integer, Boolean, Double, String, Unit>> tuple =
    applicative.map5(
        DSL.readInt(2),
        DSL.readBoolean(false),
        DSL.readDouble(2.1),
        DSL.readString("hola mundo"),
        DSL.readUnit(),
        Tuple::of
    ).fix(toFreeAp());

Kind<Id_, Tuple5<Integer, Boolean, Double, String, Unit>> map =
    tuple.foldMap(idTransform(), IdInstances.applicative());

assertEquals(Id.of(Tuple.of(2, false, 2.1, "hola mundo", unit())), map.fix(toId()));

Monad Transformers

OptionT

Monad Transformer for Option type

OptionT<IO_, String> some = OptionT.some(IO.monad(), "abc");

OptionT<IO_, String> map = some.flatMap(value -> OptionT.some(IOInstances.monad(), value.toUpperCase()));

assertEquals("ABC", map.get().fix(toIO()).unsafeRunSync());

EitherT

Monad Transformer for Either type

EitherT<IO_, Nothing, String> right = EitherT.right(IO.monad(), "abc");

EitherT<IO_, Nothing, String> map = right.flatMap(value -> EitherT.right(IOInstances.monad(), value.toUpperCase()));

assertEquals("ABC", map.get().fix(toIO()).unsafeRunSync());

StateT

Monad Transformer for State type

StateT<IO_, ImmutableList<String>, Unit> state =
  pure("a").flatMap(append("b")).flatMap(append("c")).flatMap(end());

IO<Tuple2<ImmutableList<String>, Unit>> result = state.run(ImmutableList.empty()).fix(toIO());

assertEquals(Tuple.of(listOf("a", "b", "c"), unit()), result.unsafeRunSync());

WriterT

Monad Transformer for Writer type

WriterT<Id_, Sequence<String>, Integer> writer =
    WriterT.<Id_, Sequence<String>, Integer>pure(monoid, monad, 5)
    .flatMap(value -> lift(monoid, monad, Tuple.of(listOf("add 5"), value + 5)))
    .flatMap(value -> lift(monoid, monad, Tuple.of(listOf("plus 2"), value * 2)));

assertAll(() -> assertEquals(Id.of(Integer.valueOf(20)), writer.getValue()),
          () -> assertEquals(Id.of(listOf("add 5", "plus 2")), writer.getLog()));

Kleisli

Also I implemented the Kleisli composition for functions that returns monadic values like Option, Try or Either.

Kleisli<Try_, String, Integer> toInt = Kleisli.lift(Try.monad(), Integer::parseInt);
Kleisli<Try_, Integer, Double> half = Kleisli.lift(Try.monad(), i -> i / 2.);

Kind<Try_, Double> result = toInt.compose(half).run("123");

assertEquals(Try.success(61.5), result);

Stream

An experimental version of a Stream like scala fs2 project.

StreamOf<IO_> streamOfIO = Stream.ofIO();

IO<String> readFile = streamOfIO.eval(IO.of(() -> reader(file)))
  .flatMap(reader -> streamOfIO.iterate(() -> Option.of(() -> readLine(reader))))
  .takeWhile(Option::isPresent)
  .map(Option::get)
  .foldLeft("", (a, b) -> a + "\n" + b)
  .fix(toIO())
  .recoverWith(UncheckedIOException.class, cons("--- file not found ---"));

String content = readFile.unsafeRunSync();

Effects

An experimental version of PureIO similar to ZIO.

PureIO<Console, Throwable, Unit> echoProgram =
  Console.println("what's your name?")
      .andThen(Console.readln())
      .flatMap(name -> Console.println("Hello " + name));

interface Console {

  <R extends Console> Console.Service<R> console();

  static PureIO<Console, Throwable, String> readln() {
    return PureIO.accessM(env -> env.console().readln());
  }

  static PureIO<Console, Throwable, Unit> println(String text) {
    return PureIO.accessM(env -> env.console().println(text));
  }

  interface Service<R extends Console> {
    PureIO<R, Throwable, String> readln();

    PureIO<R, Throwable, Unit> println(String text);
  }
}

Additionally, there are aliases for some PureIO special cases:

UIO<T>      =>  PureIO<?, Nothing, T>
EIO<E, T>   =>  PureIO<?, E, T>
Task<T>     =>  PureIO<?, Throwable, T>
RIO<R, T>   =>  PureIO<R, Throwable, T>
URIO<T>     =>  PureIO<R, Nothing, T>

Algebraic Effects

Also, I have implemented a version of delimited control monad based in this project. With this monad, you can implement algebraic effects. Example:

  // the effect
  interface Amb {
    Control<Boolean> flip();
  }

  // the program, if true then 2 else 3
  Control<Integer> program(Amb amb) {
    return amb.flip().map(x -> x ? 2 : 3);
  }

  @Test
  void test() {
    Control<ImmutableList<Integer>> handled = ambList(this::program);

    assertEquals(listOf(2, 3), handled.run());
  }

  <R> Control<ImmutableList<R>> ambList(Function1<Amb, Control<R>> program) {
    return new AmbList<R>().apply(amb -> program.apply(amb).map(ImmutableList::of));
  }

  final class AmbList<R> implements Handler<ImmutableList<R>, Amb>, Amb {

    @Override
    public Amb effect() { return this; }

    @Override
    public Control<Boolean> flip() {
      return use(resume ->
          resume.apply(true) // first flip return true
            .flatMap(ts -> resume.apply(false) // second flip return false
              .map(ts::appendAll)));
    }
  }

Type Classes

With higher kinded types simulation we can implement typeclases.

                       Invariant -- Contravariant
                             \
       SemigroupK           Functor -- Comonad
           |               /       \
         MonoidK   _ Applicative   Traverse -- Foldable
           |      /      |      \
       Alternative    Selective  ApplicativeError
                         |        |
  MonadWriter          Monad      |
        \________________|        |
        /          /      \      / 
  MonadState  MonadReader  MonadError_____
                               \          \
                            MonadThrow  Bracket
                                  \      /
                        Defer -- MonadDefer -- Timer
                                     |
                                   Async
                                     |
                                 Concurrent

Functor

public interface Functor<F extends > extends Invariant<F> {

  <T, R> Kind<F, R> map(Kind<F, T> value, Function1<T, R> map);
}

Applicative

public interface Applicative<F> extends Functor<F> {

  <T> Kind<F, T> pure(T value);

  <T, R> Kind<F, R> ap(Kind<F, T> value, Kind<F, Function1<T, R>> apply);

  @Override
  default <T, R> Kind<F, R> map(Kind<F, T> value, Function1<T, R> map) {
    return ap(value, pure(map));
  }
}

Selective

public interface Selective<F> extends Applicative<F> {

  <A, B> Kind<F, B> select(Kind<F, Either<A, B>> value, Kind<F, Function1<A, B>> apply);

  default <A, B, C> Kind<F, C> branch(Kind<F, Either<A, B>> value,
                                      Kind<F, Function1<A, C>> applyA,
                                      Kind<F, Function1<B, C>> applyB) {
    Kind<F, Either<A, Either<B, C>>> abc = map(value, either -> either.map(Either::left));
    Kind<F, Function1<A, Either<B, C>>> fabc = map(applyA, fb -> fb.andThen(Either::right));
    return select(select(abc, fabc), applyB);
  }
}

Monad

public interface Monad<F> extends Selective<F> {

  <T, R> Kind<F, R> flatMap(Kind<F, T> value, Function1<T, ? extends Kind<F, R>> map);

  @Override
  default <T, R> Kind<F, R> map(Kind<F, T> value, Function1<T, R> map) {
    return flatMap(value, map.andThen(this::pure));
  }

  @Override
  default <T, R> Kind<F, R> ap(Kind<F, T> value, Kind<F, Function1<T, R>> apply) {
    return flatMap(apply, map -> map(value, map));
  }

  @Override
  default <A, B> Kind<F, B> select(Kind<F, Either<A, B>> value, Kind<F, Function1<A, B>> apply) {
    return flatMap(value, either -> either.fold(a -> map(apply, map -> map.apply(a)), this::<B>pure));
  }
}

Semigroup

It represents a binary operation over a type.

@FunctionalInterface
public interface Semigroup<T> {
  T combine(T t1, T t2);
}

There are instances for strings and integers.

Monoid

Extends Semigroup adding a zero operation that represent an identity.

public interface Monoid<T> extends Semigroup<T> {
  T zero();
}

There are instances for strings and integers.

SemigroupK

It represents a Semigroup but defined for a kind, like a List, so it extends a regular Semigroup.

MonoidK

The same like SemigroupK but for a Monoid.

Invariant

public interface Invariant<F> {
  <A, B> Kind<F, B> imap(Kind<F, A> value, Function1<A, B> map, Function1<B, A> comap);
}

Contravariant

public interface Contravariant<F> extends Invariant<F> {
  <A, B> Kind<F, B> contramap(Kind<F, A> value, Function1<B, A> map);
}

Bifunctor

public interface Bifunctor<F> {
  <A, B, C, D> Kind<Kind<F, C>, D> bimap(Kind<Kind<F, A>, B> value, Function1<A, C> leftMap, Function1<B, D> rightMap);
}

Profunctor

public interface Profunctor<F> {
  <A, B, C, D> Kind<Kind<F, C>, D> dimap(Kind<Kind<F, A>, B> value, Function1<C, A> contramap, Function1<B, D> map);
}

Applicative Error

public interface ApplicativeError<F, E> extends Applicative<F> {

  <A> Kind<F, A> raiseError(E error);

  <A> Kind<F, A> handleErrorWith(Kind<F, A> value, Function1<E, ? extends Kind<F, A>> handler);
}

Monad Error

public interface MonadError<F, E> extends ApplicativeError<F, E>, Monad<F> {

  default <A> Kind<F, A> ensure(Kind<F, A> value, Producer<E> error, Matcher1<A> matcher) {
    return flatMap(value, a -> matcher.match(a) ? pure(a) : raiseError(error.get()));
  }
}

Monad Throw

public interface MonadThrow<F> extends MonadError<F, Throwable> {

}

MonadReader

public interface MonadReader<F, R> extends Monad<F> {

  Kind<F, R> ask();

  default <A> Kind<F, A> reader(Function1<R, A> mapper) {
    return map(ask(), mapper);
  }
}

MonadState

public interface MonadState<F, S> extends Monad<F> {
  Kind<F, S> get();
  Kind<F, Unit> set(S state);

  default Kind<F, Unit> modify(Operator1<S> mapper) {
    return flatMap(get(), s -> set(mapper.apply(s)));
  }

  default <A> Kind<F, A> inspect(Function1<S, A> mapper) {
    return map(get(), mapper);
  }

  default <A> Kind<F, A> state(Function1<S, Tuple2<S, A>> mapper) {
    return flatMap(get(), s -> mapper.apply(s).applyTo((s1, a) -> map(set(s1), x -> a)));
  }
}

MonadWriter

public interface MonadWriter<F, W> extends Monad<F> {

  <A> Kind<F, A> writer(Tuple2<W, A> value);
  <A> Kind<F, Tuple2<W, A>> listen(Kind<F, A> value);
  <A> Kind<F, A> pass(Kind<F, Tuple2<Operator1<W>, A>> value);

  default Kind<F, Unit> tell(W writer) {
    return writer(Tuple.of(writer, unit()));
  }
}

Comonad

public interface Comonad<F> extends Functor<F> {

  <A, B> Kind<F, B> coflatMap(Kind<F, A> value, Function1<Kind<F, A>, B> map);

  <A> A extract(Kind<F, A> value);

  default <A> Kind<F, Kind<F, A>> coflatten(Kind<F, A> value) {
    return coflatMap(value, identity());
  }
}

Foldable

public interface Foldable<F> {

  <A, B> B foldLeft(Kind<F, A> value, B initial, Function2<B, A, B> mapper);

  <A, B> Eval<B> foldRight(Kind<F, A> value, Eval<B> initial, Function2<A, Eval<B>, Eval<B>> mapper);
}

Traverse

public interface Traverse<F> extends Functor<F>, Foldable<F> {

  <G, T, R> Kind<G, Kind<F, R>> traverse(Applicative<G> applicative, Kind<F, T> value,
      Function1<T, ? extends Kind<G, R>> mapper);
}

Semigroupal

public interface Semigroupal<F> {

  <A, B> Kind<F, Tuple2<A, B>> product(Kind<F, A> fa, Kind<F, B> fb);
}

Defer

public interface Defer<F> {

  <A> Kind<F, A> defer(Producer<Kind<F, A>> defer);
}

Bracket

public interface Bracket<F, E> extends MonadError<F, E> {

  <A, B> Kind<F, B> bracket(Kind<F, A> acquire, Function1<A, ? extends Kind<F, B>> use, Consumer1<A> release);
}

MonadDefer

public interface MonadDefer<F> extends MonadThrow<F>, Bracket<F, Throwable>, Defer<F>, Timer<F> {

  default <A> Kind<F, A> later(Producer<A> later) {
    return defer(() -> Try.of(later::get).fold(this::raiseError, this::pure));
  }
}

Async

public interface Async<F> extends MonadDefer<F> {
  
  <A> Kind<F, A> async(Consumer1<Consumer1<Try<A>>> consumer);

}

Timer

public interface Timer<F> {

  Kind<F, Unit> sleep(Duration duration);
}

FunctionK

It represents a natural transformation between two different kinds.

public interface FunctionK<F, G> {
  <T> Kind<G, T> apply(Kind<F, T> from);
}

Optics

         __Iso__
        /       \
    Lens        Prism
        \       /
         Optional

Iso

An Iso is an optic which converts elements of some type into elements of other type without loss. In other words, it's isomorphic.

Point point = new Point(1, 2);

Iso<Point, Tuple2<Integer, Integer>> pointToTuple = 
  Iso.of(p -> Tuple.of(p.x, p.y), 
         t -> new Point(t.get1(), t.get2()));

assertEquals(point, pointToTuple.set(pointToTuple.get(point)));

Lens

A Lens is an optic used to zoom inside a structure. In other words, it's an abstraction of a setter and a getter but with immutable objects.

Lens<Employee, String> nameLens = Lens.of(Employee::getName, Employee::withName);

Employee pepe = new Employee("pepe");

assertEquals("pepe", nameLens.get(pepe));
assertEquals("paco", nameLens.get(nameLens.set(pepe, "paco")));

We can compose Lenses to get deeper inside. For example, if we add an attribute of type Address into Employee. We can create a lens to access the city name of the address of the employee.

Lens<Employee, Address> addressLens = Lens.of(Employee::getAddress, Employee::withAddress);
Lens<Address, String> cityLens = Lens.of(Address::getCity, Address::withCity);
Lens<Employee, String> cityAddressLens = addressLens.compose(cityLens);

Employee pepe = new Employee("pepe", new Address("Madrid"));

assertEquals("Madrid", cityAddressLens.get(pepe));

Prism

A Prism is a lossless invertible optic that can see into a structure and optionally find a value.

Function1<String, Option<Integer>> parseInt = ...; // is a method that only returns a value when the string can be parsed

Prism<String, Integer> stringToInteger = Prism.of(parseInt, String::valueOf);

assertEquals(Option.some(5), stringToInteger.getOption("5"));
assertEquals(Option.none(), stringToInteger.getOption("a"));
assertEquals("5", stringToInteger.reverseGet(5));

Optional

An Optional is an optic that allows to see into a structure and getting, setting like a Lens an optional find a value like a Prism.

Optional<Employee, Address> addressOptional = Optional.of(
  Employee::withAddress, employee -> Option.of(employee::getAddress)
);

Address madrid = new Address("Madrid");
Employee pepe = new Employee("pepe", null);

assertEquals(Option.none(), addressOptional.getOption(pepe));
assertEquals(Option.some(madrid), addressOptional.getOption(addressOptional.set(pepe, madrid)));

Composition

Optional Prism Lens Iso
Optional Optional Optional Optional Optional
Prism Optional Prism Optional Prism
Lens Optional Optional Lens Lens
Iso Optional Prism Lens Iso

Equal

This class helps to create readable equals methods. An example:

@Override
public boolean equals(Object obj) {
  return Equal.<Data>of()
    .comparing(Data::getId)
    .comparing(Data::getValue)
    .applyTo(this, obj);
}

Stargazers over time

Stargazers over time

License

purefun is released under MIT license

purefun's People

Contributors

dependabot[bot] avatar gitter-badger avatar tonivade avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

purefun's Issues

Future is using await() all the time

If the used thread pool is fixed thread, then is possible to lead to a deadlock between threads, waiting for the result of other thread.

So I need to find a way to use callbacks instead of locks.

Are you using purefun? Please tell me how!!

If you are using purefun, It would be awesome if you tell me how you are using it.

  • Environment:
  • Purpose:
  • Which features are you using:
  • Which features do you miss:

If you want to help me, please write a comment to this issue with this information.

Thanks a lot!

Try to implement ZLayer

ZLayer

It seems that is samething that receives an environment and the result is other enviroment

public interface ZLayer<R, E, S> {
  Either<E, S> provide(R input);
}

Add managed resources to ZIO and IO

Something like this

class Resource<A> {
  IO<A> acquire;
  Consumer1<A> release;

  Resource(IO<A> acquire, Consumer1<A> release) {
    this.acquire = acquire;
    this.release = release;
  }

  public <B> IO<B> use(Function1<A, IO<B>> use) {
    return IO.bracket(acquire, use, release);
  }
}

Improve Free Monad

First time I tried to implement this, in general, I didn't know what I was doing. Now after implementing typeclases and other stuff, I feel that this can be improved.

Async Typeclass implementation for IO

Don't know how to do it, but It should be possible to implement it anyway.

interface Async<F extends Kind> extends MonadDefer<F> {
  <A> Higher1<F, A> async(Function1<Consumer1<Try<A>>, Unit> callback);
  <A> Higher1<F, A> asyncF(Function1<Consumer1<Try<A>>, Higher1<F, Unit>> callback);
}

Generate sealed classes using annotation processor

For example, in Option interface, we want to make sure that only two implementations of Option are available, Some and None.

Right now I'm doing a trick to forbid other classes may implement the Option interface.

@HigherKind
public interface Option extends OptionOf<T> {

  OptionModule getModule();

  final class Some implements Option<T> {
      //...
      public OptionModule getModule() {
         throw new UnsupportedOperationException();
      }
  }

  final class None implements Option<T> {
      //...
      public OptionModule getModule() {
         throw new UnsupportedOperationException();
      }
  }
}

interface OptionModule {}

OptionModule is not visible outside, so you cannot implement this interface outside this package.

But I think it's possible to implement the same using annotation processing.

@HigherKind(sealed = true)
public interface Option extends OptionOf<T> {

  //..

  final class Some implements SealedOption<T> {
    //...
  }

  final class None implements SealedOption<T> {
      //...
  }
}

The this code will be generated

public interface OptionOf<T> extends Kind<Option_, T> {

  SealedOption<T> youShallNotPass();

  //...
}

// package private
interface SealedOption<T> extends Option<T> {
  @Override
  default SealedOption<T> youShallNotPass() {
    throw new UnsupportedOperationException();
  }
}

This way, if you try to implement Option outside, the compiler will fail, because SealedOption<T> is not visible.

Split in different modules

Now the project contains a lot of code, and is would be nice to split into modules:

  • core: main interfaces and types (Option, Try, Either, Validation)
  • transformers: for monad transformers
  • free: for free monad
  • io: for io monad
  • stream: for stream
  • concurrent: for futures
  • optics: for optics
  • typeclasses: for typeclasses and instances

foldMapping in zio effect doesn't look good

Currently there's a hack to wrap errors in a exception, but in any case I cannot use a MonadError because in the cases that there's a map in error for example mapError or bimap, i will need two different instances one for E and one for F, and there's no mapError method in MonadError and I don't know if it's even possible.

StackOverflowError using Scheduler

Scheduler effect and typeclass implementation are not stack safe

...
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.lambda$foldM$2(Function1,Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:99)
0x000070710f00d6d8.run(Object) (Unknown Source:-1)
Function1.apply(Object) (/purefun-core-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.core/Function1.class:32)
PureIO.runAsync(Object,PureIO,PureIOConnection,CallStack,Promise) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:556)
PureIO.runAsync(Object,PureIO,PureIOConnection) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:513)
PureIO.runAsync(Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:55)
PureIO.provide(Object) (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/PureIO.class:51)
UIO.unsafeRunSync() (/purefun-effect-5.0-SNAPSHOT.jar/com.github.tonivade.purefun.effect/UIO.class:61)
perftest.run(UIO,String) (/home/vant/Descargas/kkk/perftest.java:251)
perftest.hashMap() (/home/vant/Descargas/kkk/perftest.java:95)
perftest.main(String[]) (/home/vant/Descargas/kkk/perftest.java:73)

algebraic effects

Hi!

really impressed by your work, especially the use of @FunctionalInterfaces is very inspiring.
I've been working a lot with heirarchical context problems lately, and your code has been somewhat useful thought experiment

I was wondering, have you considered attempting to implement algebraic effects in this style?
https://overreacted.io/algebraic-effects-for-the-rest-of-us/
https://b-studios.de/scala-effekt/guides/getting-started.html
https://www.researchgate.net/publication/328511790_Effect_handlers_for_the_masses

Improve annotation processor

Instead of use Higher2 or Higher3 to encode kinds of 2, 3 parameters, it would be better solution something like this:

public class Whatever<A, B> implements WhateverOf<A, B> {}

Since WhateverOf is:

public interface WhateverOf<A, B> extends Higher1<Higher1<Whatever_, A>, B> {

  static <A, B> Whatever<A, B> narrowK(Higher1<Higher1<Whatever_, A>, B> hkt) {
    return (Whatever<A, B>) hkt;
  }
}

And Whatever_ as usual:

public final class Whatever_ implements Kind {}

This would make useless the Higher2 and Higher3 interfaces, and also we can refactor Higher1 to Kind, and Kind to Witness.

public interface Witness {}

public interface Kind<F extends Witness, A> extends Witness {}

public class Whatever<A, B> implements WhateverOf<A, B> {}

public interface WhateverOf<A, B> extends Kind<Kind<Whatever_, A>, B> {

  static <A, B> Whatever<A, B> narrowK(Kind<Kind<Whatever_, A>, B> hkt) {
    return (Whatever<A, B>) hkt;
  }
}

public final class Whatever_ implements Witness {}

Experiment with real concurrency in IO, ZIO, Stream

Now IO, Stream, and ZIO are developed without thinking about concurrency, but I think that is time to start to work on it.

For example, IO has a method called toFuture() that transform the IO to a Future, but acts only in one level, if IO is a program of some linked IO's using flatMap, only the whole program is executed in another thread. The idea is that we can launch each step of the chain in different threads.

Implement Scheduler

Something like this

  @Test
  public void repeat(@Mock Consumer1<String> console) {
    ZIO<Nothing, Throwable, Unit> print = ZIO.exec(() -> console.accept("hola"));
    ZIO<Nothing, Throwable, Integer> repeat = print.repeat(Schedule.repeat(2));

    Either<Throwable, Integer> provide = repeat.provide(nothing());

    assertEquals(Either.right(2), provide);
    verify(console, times(3)).accept("hola");
  }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.