/*
* Copyright (c) 2022, Antonio Gabriel Muñoz Conejo <antoniogmc at gmail dot com>
* Distributed under the terms of the MIT License
*/
package com.github.tonivade.vavr.effect;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.UnaryOperator;
import io.vavr.Function1;
import io.vavr.Tuple2;
public final class Reference<A> {
private final AtomicReference<A> value;
private Reference(AtomicReference<A> value) {
this.value = Objects.requireNonNull(value);
}
public IO<A> get() {
return IO.task(value::get);
}
public IO<Unit> set(A newValue) {
return IO.task(() -> { value.set(newValue); return Unit.unit(); });
}
public <B> IO<B> modify(Function1<A, Tuple2<B, A>> change) {
return IO.task(() -> {
var loop = true;
B result = null;
while (loop) {
A current = value.get();
var tuple = change.apply(current);
result = tuple._1();
loop = !value.compareAndSet(current, tuple._2());
}
return result;
});
}
public IO<Unit> lazySet(A newValue) {
return IO.task(() -> { value.lazySet(newValue); return Unit.unit(); });
}
public IO<A> getAndSet(A newValue) {
return IO.task(() -> value.getAndSet(newValue));
}
public IO<A> updateAndGet(UnaryOperator<A> update) {
return IO.task(() -> value.updateAndGet(update::apply));
}
public IO<A> getAndUpdate(UnaryOperator<A> update) {
return IO.task(() -> value.getAndUpdate(update::apply));
}
public static <A> IO<Reference<A>> make(A value) {
return IO.pure(of(value));
}
public static <A> Reference<A> of(A value) {
return new Reference<>(new AtomicReference<>(value));
}
@Override
public String toString() {
return String.format("Ref(%s)", value.get());
}
}