Last active
July 17, 2022 20:22
-
-
Save 7h3kk1d/231870c576d146f6f47b36fa924261ca to your computer and use it in GitHub Desktop.
Higher Kinded Data Pattern in Lambda
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
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(); | |
} | |
} |
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
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