Skip to content

Instantly share code, notes, and snippets.

@7h3kk1d
Last active July 17, 2022 20:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 7h3kk1d/231870c576d146f6f47b36fa924261ca to your computer and use it in GitHub Desktop.
Save 7h3kk1d/231870c576d146f6f47b36fa924261ca to your computer and use it in GitHub Desktop.
Higher Kinded Data Pattern in Lambda
public class Person<F extends MonadRec<?, F>> {
MonadRec<Integer, F> age;
MonadRec<String, F> name;
public Person(MonadRec<Integer, F> age, MonadRec<String, F> name) {
this.age = age;
this.name = name;
}
public static Person<ReaderT<String, Either<String, ?>, ?>> personValidation() {
throw new UnsupportedOperationException();
}
public static Person<Identity<?>> person(Integer age, String name) {
return new Person<>(new Identity<>(age),
new Identity<>(name));
}
public <FA extends MonadRec<Integer, F>> FA getAge() {
return age.coerce();
}
public <FA extends MonadRec<String, F>> FA getName() {
return name.coerce();
}
public <FP extends MonadRec<Person<Identity<?>>, F>> FP traverse() {
return liftA2(Person::person, age, name).coerce();
}
public interface NatTrans<F extends Functor<?, F>, G extends Functor<?, G>> {
<A, FA extends Functor<A, F>, GA extends Functor<A, G>> GA transform(FA fa);
}
public <G extends MonadRec<?, G>> Person<G> transform(NatTrans<F, G> trans) {
return new Person<>(trans.transform(age), trans.transform(name));
}
public static void main(String[] args) {
String person_json = "serialized person";
Person<ReaderT<String, Either<String, ?>, ?>> personValidation = personValidation();
Either<String, Integer> eitherAge =
personValidation
.<ReaderT<String, Either<String, ?>, Integer>>getAge()
.runReaderT(person_json);
Person<Either<String, ?>> parsedPerson = personValidation.transform(new NatTrans<ReaderT<String, Either<String, ?>, ?>, Either<String, ?>>() {
@Override
public <A, FA extends Functor<A, ReaderT<String, Either<String, ?>, ?>>, GA extends Functor<A, Either<String, ?>>> GA transform(FA fa) {
ReaderT<String, Either<String, ?>, A> readerT = fa.coerce();
Either<String, A> parsed = readerT.runReaderT(person_json);
return parsed.coerce();
}
});
Either<String, String> eitherName = parsedPerson.getName();
Person<Maybe<?>> maybePerson = parsedPerson.transform(new NatTrans<Either<String, ?>, Maybe<?>>() {
@Override
public <A, FA extends Functor<A, Either<String, ?>>, GA extends Functor<A, Maybe<?>>> GA transform(FA fa) {
Maybe<A> aMaybe = fa.<Either<String, A>>coerce()
.toMaybe();
return aMaybe.coerce();
}
});
Maybe<Integer> optionalAge = maybePerson.getAge();
Either<String, Person<Identity<?>>> fullyValidatedPerson = parsedPerson.traverse();
Person<Identity<?>> identityPerson = fullyValidatedPerson.orThrow(RuntimeException::new);
Integer age = identityPerson.<Identity<Integer>>getAge().runIdentity();
String name = identityPerson.<Identity<String>>getName().runIdentity();
}
}
public interface Person<F extends MonadRec<?, F>> {
MonadRec<Integer, F> age();
MonadRec<String, F> name();
default <FP extends MonadRec<PersonRecord, F>> FP traverse() {
return liftA2(PersonRecord::new, this.age(), this.name()).coerce();
}
default <G extends MonadRec<?, G>> Person<G> transform(NatTrans<F, G> trans) {
return new Person<>() {
@Override
public MonadRec<Integer, G> age() {
return trans.transform(Person.this.age()).coerce();
}
@Override
public MonadRec<String, G> name() {
return trans.transform(Person.this.name()).coerce();
}
};
}
interface NatTrans<F extends Functor<?, F>, G extends Functor<?, G>> {
<A, FA extends Functor<A, F>, GA extends Functor<A, G>> GA transform(FA fa);
}
record PersonRecord(Integer actualAge, String actualName) implements Person<Identity<?>> {
public Identity<Integer> age() {
return new Identity<>(this.actualAge).coerce();
}
public Identity<String> name() {
return new Identity<>(this.actualName).coerce();
}
}
class PersonValidation implements Person<ReaderT<Map<String, Object>, Either<String, ?>, ?>> {
@Override
public ReaderT<Map<String, Object>, Either<String, ?>, Integer> age() {
return ReaderT.readerT(map -> maybe(map.get("age"))
.toEither(() -> "age not found")
.flatMap(obj -> hasType(obj, Integer.class, "age")));
}
@Override
public ReaderT<Map<String, Object>, Either<String, ?>, String> name() {
return ReaderT.readerT(map -> maybe(map.get("name"))
.toEither(() -> "name not found")
.flatMap(obj -> hasType(obj, String.class, "name")));
}
@SuppressWarnings("unchecked")
private static <T> Either<String, T> hasType(Object obj, Class<T> clazz, String name) {
if(clazz.isInstance(obj))
return right((T) obj);
else
return left(name + " was the wrong type " + obj.getClass().getName());
}
}
public static void main(String[] args) {
Map<String, Object> map = Map.of("age", 20,
"name",
"first");
PersonValidation personValidation = new PersonValidation();
Either<String, Integer> eitherAge = personValidation.age().runReaderT(map);
System.out.println(eitherAge);
Person<Either<String, ?>> transform = personValidation.transform(new NatTrans<ReaderT<Map<String, Object>, Either<String, ?>, ?>, Either<String, ?>>() {
@Override
public <A, FA extends Functor<A, ReaderT<Map<String, Object>, Either<String, ?>, ?>>, GA extends Functor<A, Either<String, ?>>> GA transform(FA fa) {
return fa.<ReaderT<Map<String, Object>, Either<String, ?>, A>>coerce()
.runReaderT(map)
.coerce();
}
});
Either<String, String> name = transform.name().coerce();
System.out.println(name);
ReaderT<Map<String, Object>, Either<String, ?>, PersonRecord> traverse = personValidation
.traverse();
Either<String, PersonRecord> person = traverse.runReaderT(map);
System.out.println(person);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment