derive4j / derive4j Goto Github PK
View Code? Open in Web Editor NEWJava 8 annotation processor and framework for deriving algebraic data types constructors, pattern-matching, folds, optics and typeclasses.
Java 8 annotation processor and framework for deriving algebraic data types constructors, pattern-matching, folds, optics and typeclasses.
I've noticed when having something like.
. . .
interface Cases<R,A> {
R DoSomething(String x, F<Integer,A> k);
}
. . .
That it generates a Type constructor DoSomething(String x)
without the k
. It uses the Identity function for us.
However this might not always be the only function we would want to use.
For Example. Defining an F-algebra for the use in a free monad. Then defining a Functor instance for the F-algrebra becomes impossible, because we do not have DoSomething(String x, F<Integer,A> k)
type constructor.
Maybe auto-generate both type constructors?
What a nice processor!
I would ask you use a more descriptive name than @Data
for annotation:
@Data
and grok something interesting is happening.Data
class, especially after imports.Data
class, which does something rather different.You have many fine alternative choices for naming the annotation. First to my mind is @ADT
. A web search on "ADT" turns up near the top the right Wikipedia page to understand some background on this library's goals.
Cheers,
--binkley
When compiling with ecj, total pattern matching syntax does not follow definition order in source file.
Also some generated code does not compile due poor type inference.
For class that implements https://github.com/derive4j/hkt/blob/master/src/main/java/org/derive4j/hkt/__.java
Now I'm not sure of what the method name should be... (configurable?)
Can be handy if a data constructor has a lot of fields, and you want to insure the user of your data structure supplies the fields in the correct order. (avoid mixing up fields with the same type, like width
and height
)
Would be it possible to add support for varargs:
interface Cases<R> {
R develop(Job job, Job... dependencies);
R review(Job job, Job... dependencies);
}
generators constructors like:
public static JobType develop(Job job, Job[] dependencies) {
...
It would be handy if the constructor method matched the vararg definition.
That will probably be my greatest achievement!
Jamais deux sans trois,
Re-reading my example :
abstract class Seminar<T> {
Seminar() {}
interface Cases<R, T> {
R Init(SData.Core core, F<Init, T> __);
R Temp(SData.Core core, SData.Add1 add1, F<Temp, T> __);
}
public static final class Init { private Init() {} }
public static final class Temp { private Temp() {} }
}
I realize that the free F
type (any functional interface would do, right ?) used to associate the T
type parameter with a concrete (Init
or Temp
) type could be profitably (for the newcomer) replaced by a fixed Id
type provided by derive4j.
What do you think ? Wouldn't it make the intent of that mysterious additional parameter clearer to the boeotian (that I once was) ?
Hi Jean-Baptiste,
Let's say we have the following GADT :
@data
interface Seminar<T> {
interface Cases<R, T> {
R Init(SData.Core core, TypeEq<Init, T> __);
R Temp(SData.Core core, SData.Add1 add1, TypeEq<Temp, T> __);
}
<R> R match(Cases<R, T> cases);
enum Init {}
enum Temp {}
}
then the following function
// getter not returning an Option<SData.Add1>
public static SData.Add1 getAdd1(Seminar<Temp> temp) {
return Seminars.getAdd1(temp).some(); // cannot fail since we have a specific Seminar<Temp>
}
has to be written by hand, as you can see.
Would it be possible, in the case of GADTs, to generate total getters for specific cases ?
It would often be nice to have sensible ToString implementations on the generated classes. One way of providing toString() is to use lombok's @tostring annotation on the generated class. This could be enabled by being able to specify which annotations should apply to the generated classes.
This is a common oversight when creating inner class. See #14.
Use the flavour F0/Supplier or F/Function interfaces instead. This will save some classes and facilitate composition.
Given:
@Data
abstract class Nat {
interface Cases<R> {
R Zero();
R Suc(Nat nat);
}
public abstract <R> R match(Cases<R> cases);
private static Function<Nat, Integer> eval =
Nats.cases().Zero( () -> 0)
.Suc(nat -> eval(nat) + 1);
public static Integer eval(Nat nat) {
return eval.apply(nat);
}
}
When executing
public class Main {
public static void main(String[] args) {
Nat nat = Suc(Suc(Suc(Suc(Zero()))));
System.out.println(eval(nat));
}
}
You get
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.jomaveger.lambda.adt.Nats.<clinit>(Nats.java:10)
at org.jomaveger.lambda.main.Main.main(Main.java:15)
Caused by: java.lang.NullPointerException
at org.jomaveger.lambda.adt.Nat.<clinit>(Nat.java:21)
... 2 more
Due to:
To avoid this, the Zero constructor must not be eagerly initialized.
Hey, me again,
the following haskell idiom seems currently not expressible using derive4j :
data Stream a = forall s. Stream (s -> Step a s) s
Would it be possible to allow that kind of construct :
@Derive4J(flavour = Flavour.FJ)
abstract class Stream<A> {
interface Cases<R, A, S> {
R Stream(Unlifted<S> s, F<S, Step<A, S>> stepper, S init);
}
abstract <R, S> R match(Cases<R, A, S> cases);
}
or better
@Derive4J(flavour = Flavour.FJ)
abstract class Stream<A> {
interface Cases<R, A> {
<S> R Stream(Unlifted<S> s, F<S, Step<A, S>> stepper, S init);
}
abstract <R> R match(Cases<R, A> cases);
}
What do you think ?
Fields whose type correspond to a type variable are (wrongly) compared using reference equality instead of Object#equals.
Currently, by default, the generated class is the name of the class + 's'.
This issue is about making the default name to better follow English grammar for plural:
There is also lots of other special cases but not sure they are worth it.
today there is only
Function<ADT, R> otherwise(Supplier<R> expression)
I think it should be renamed into
Function<ADT, R> otherwiseEval(Supplier<R> expression)
And then we can add:
Function<ADT, R> otherwise(R value)
Function<ADT, Option<R>> otherwiseNone()
Function<ADT, Either<L, R>> <L> otherwiseLeft(L leftValue)
Function<ADT, Validation<E, R>> <E> otherwiseFail(E error)
What others think about that feature? (are naming good?)
Last week I was experimenting with reworking some existing $work code ( using jADT ) to use derive4j and whilst it worked well, and the code translated fairly seamlessly, I started noticed weird parse/completion errors in IntelliJ.
It seems IntelliJ by default stops parsing files if there over 2500 lines long, and the derive4j generated source file in question was something like 60,000 lines.
In this particular instance, once an object has been created, we have no desired to have mutations, so don't really need any of the set/mod support functions to get generated ( also saving a lot of lambdas being compiled ).
As a simple solution ( other than increasing the rather hidden setting for IntelliJ completion ) was to have a new Flavour.None for derive4j that prevented the generation of get/set/mod methods, leaving you only with the constructors.
Would something like this be useful to anyone else? Or would a more generic (disable set, disable mod, disable get) generation control across flavours be more useful?
For a given ADT, EnumAdt
, where all constructors take 0 parameters, then derive4j should derive:
public static List<EnumAdt> values();
public static Option<EnumAdt> forConstructor(String);
public static String constructorName(EnumAdt);
public static int constructorOrdinal(EnumAdt);
The last two could be generated for all ADT (can be useful for serialization).
Better naming suggestions are welcomed!
It well be more consistent with usual FP terminology. Currently we re-use name of the 'match' method. but it is misleading as pattern matching return a function.
Components of the annotation would then be overridable:
package and class annotated deeper in the package hierarchy override the configuration found in annotated packages closer to the root.
This will alleviate the need to repeat the same @Data
configuration over and over on each data type in a given project.
Also the name of the generated class will support template, ie something like @Derive(inClass="{ClassName}Impl")
.
The following:
@Data(value = @Derive(inClass = "Draw2DFImpl", withVisibility = Visibility.Package), flavour = Flavour.FJ)
public abstract class Draw2DF<M,A> implements __2<Draw2DF.Mu,M,A> {
public static class Mu {}
public static <M,A> Draw2DF<M,A> narrow(__<__<Mu,M>,A> a) {
return (Draw2DF<M,A>)a;
}
public interface Cases<R,M,A> {
R GetTextSize(String text, F<V2<Double>,A> textSizeK);
R GetTextXWrappedHeight(String text, Double width, F<Double,A> textHeightK);
R WithScale(Double scale, FreeT<__<Mu,M>,M,A> drawing);
R DrawTextXWrapped(String text, Double width, A next);
R DrawText(Vector2D pos, String text, Double alignmentX, Double alignmentY, A next);
R Draw(Shape2D shape, A next);
R Fill(Shape2D shape, A next);
R GetImageSize(Draw2D2.ImageSource imgSrc, F<V2<Double>,A> imageSizeK);
R DrawImage(Draw2D2.ImageSource imgSrc, Vector2D pos, Double alignmentX, Double alignmentY, A next);
R DrawImageScaled(Draw2D2.ImageSource imgSrc, Vector2D pos, Double width, Double height, Double alignmentX, Double alignmentY, A next);
R GetCurrentFont(F<Draw2D2.Font,A> fontK);
R WithFont(Draw2D2.Font font, FreeT<__<Mu,M>,M,A> drawing);
R UseColour(Draw2D.Colour colour, FreeT<__<Mu,M>,M,A> drawing);
R InSpace(Axes2D axes, FreeT<__<Mu,M>,M,A> drawing);
}
public abstract <R> R match(Cases<R,M,A> cases);
}
Has the error: Error:java: com.sm.fp.graphics.Draw2DF: tag M cannot be used for both 'M' and 'M_'
But when wrapped into a new type (Drawing
), the following this works fine:
@Data(value = @Derive(inClass = "Draw2DFImpl", withVisibility = Visibility.Package), flavour = Flavour.FJ)
public abstract class Draw2DF<M,A> implements __2<Draw2DF.Mu,M,A> {
public static class Mu {}
public static <M,A> Draw2DF<M,A> narrow(__<__<Mu,M>,A> a) {
return (Draw2DF<M,A>)a;
}
public interface Cases<R,M,A> {
R GetTextSize(String text, F<V2<Double>,A> textSizeK);
R GetTextXWrappedHeight(String text, Double width, F<Double,A> textHeightK);
R WithScale(Double scale, Drawing<M,A> drawing);
R DrawTextXWrapped(String text, Double width, A next);
R DrawText(Vector2D pos, String text, Double alignmentX, Double alignmentY, A next);
R Draw(Shape2D shape, A next);
R Fill(Shape2D shape, A next);
R GetImageSize(Draw2D2.ImageSource imgSrc, F<V2<Double>,A> imageSizeK);
R DrawImage(Draw2D2.ImageSource imgSrc, Vector2D pos, Double alignmentX, Double alignmentY, A next);
R DrawImageScaled(Draw2D2.ImageSource imgSrc, Vector2D pos, Double width, Double height, Double alignmentX, Double alignmentY, A next);
R GetCurrentFont(F<Draw2D2.Font,A> fontK);
R WithFont(Draw2D2.Font font, Drawing<M,A> drawing);
R UseColour(Draw2D.Colour colour, Drawing<M,A> drawing);
R InSpace(Axes2D axes, Drawing<M,A> drawing);
}
public abstract <R> R match(Cases<R,M,A> cases);
public static class Drawing<M,A> {
private final FreeT<__<Mu,M>,M,A> toFreeT;
private Drawing(FreeT<__<Mu,M>,M,A> toFreeT) {
this.toFreeT = toFreeT;
}
public static <M,A> Drawing<M,A> drawing(FreeT<__<Mu,M>,M,A> toFreeT) {
return new Drawing<>(toFreeT);
}
}
}
There is some possibilities that the generated classes does not compile due to clash of identifers (eg. identifier originating from user code clash with hard-coded identifier in generated code).
This should not happen in most cases, but we should ensure hygienism of all generated code.
Will be facilitated by square/javapoet#338
Should go hand in hand with #2.
Additionally from the current synchronized-based implementation, two alternative implementations should be available and configurable via annotation:
AtomicReference<Adt>
AtomicReference<WeakReference<Adt>>
which allow better throughput than the synchronized based implementation at the price of possible concurrent evaluation.
when the field is not present for all constructors.
First code showcase:
public abstract <R> R match(Cases<X> cases);
Should probably be Cases<R>
I was thinking it would be handy to have some means of providing a validator over the ADT values, I often use this feature of Immutables and keep thinking it would be useful here.
Currently, derive4j checks for nullness in its created instances, but it would be nice if we could define something like:
protected|public void validate() {
this.match(....)
}
or
protected|public static Predicate<MyAdtType> validator = it -> ....;
which if existed, gets called before returning the constructed value.
Thoughts?
Since the time I closed #8, I encountered quite a few place where the overload was ambiguous (at least for javac) and had to use explicit type annotation to fix them. This was annoying and I'm thinking of avoiding overloading for the next release.
This means adding the suffix _
to the overload that take a plain (constant) value as argument.
This suffix is consistent with the syntax in haskell/scala where _
is a place holder for an ignored value.
ie. derive4j must look for not implemented abstract method also in the interfaces and/or super class, recursively.
This allow to use techniques described in http://ropas.snu.ac.kr/~bruno/papers/ecoop2012.pdf
(and also rename module 'exemple' => 'example')
Although not necessary if you never use null (compiler plugin, anyone?), a mechanism ensuring that null never slip into a data-structure is expected by some user.
Null checks in generated code will be triggered by:
@Data(arguments = checkedNotNull)
Consider the following example:
@Data(flavour = Flavour.JDK)
public abstract class MyMap<A, B> {
public interface MyMapVisitor<A, B> {
MyMap<A, B> EmptyMap();
MyMap<A, B> Insert(Pair<A, B> argPair, MyMap<A, B> argMap);
}
public abstract MyMap<A, B> match(MyMapVisitor<A, B> visitor);
}
and I get the following on mvn clean compile
:
Method must have one, and only one, type variable (without bounds)
that should also be the method return type.
which is from AdtParser#144.
What am I doing wrong?
Given these classes :
@Data(flavour = Flavour.FJ)
public abstract class Product<T> {
// all the stuff needed...
// ...and then
@Data(flavour = Flavour.FJ)
public abstract class Seminar<T> {
// all the stuff needed...
} // end of Seminar
} // end of Product
only the Products
class gets generated. It would be nice if annotated inner classes could be parsed and get their utilities generated just as top level ones.
It would be nice to be able to pattern match on enum. If derive4j detect that the annotated class is an enum then it should skip constructors generation.
Apply DRY seriously: code base has too much duplicated code blocks.
Add missing doc comments for not trivial stuff.
If there is only one constructor, generated code fails to compile.
It would be nice to automatically generate Jackson serialization and deserialization code for Derive4J defined ADTs. The encoding seems fairly straightforward: for any subclass, serialize its attributes int a JSON object and additionally add a "type" field that is a string encoding of the variant name (i.e. the tag).
There are probably some corner cases, like if a subclass has an attribute named "type". So there may need to be an option to define the name of the field that holds the tag. The default could be "type".
This of course should be optional (opt-in), so users who don't want this code generated are not affected.
for projects stuck on Java 7
see report in datatyped/pfds-java@3487a21#commitcomment-16018772 by @rdegnan
After playing with Derive4J for even just an evening, I think having support for JavaSlang from @danieldietrich would be a great addition once the 2.0 release it made.
This would be a natural fit alongside the other flavours - reusing JavaSlangs Option
and Function2
. Adding generators for tryEmailAddress
along side getEmailAddress
when using JavaSlang flavour may also be a potentially nice addition.
I changed one of my objects to include a boolean
and got this:
[INFO] Compiling 9 source files to /Users/amrk/IdeaProjects/upstream/halbuilder/halbuilder-core/target/classes
java.lang.IllegalArgumentException: boolean
at com.sun.tools.javac.model.JavacTypes.getDeclaredType0(JavacTypes.java:252)
at com.sun.tools.javac.model.JavacTypes.getDeclaredType(JavacTypes.java:222)
at org.derive4j.processor.DeriveUtilsImpl.resolve(DeriveUtilsImpl.java:137)
at org.derive4j.processor.derivator.GettersDerivator.visitorDispatchLensGetterImpl(GettersDerivator.java:197)
at org.derive4j.processor.derivator.GettersDerivator.lambda$generateLensGetter$10(GettersDerivator.java:173)
I guess derive4j doesn't support primitives? Should it? Or should gracefully fail, I switched to Boolean
for now anyway.
You need smart constructors for your data type when the constraints of your model cannot be made practical by the type system. Eg. you need to validate at runtime the input needed to construct the data type with constructors of the form input -> Option ADT
instead of normal input -> ADT
constructors generated by derive4J.
If that happens then you cannot expose constructors and setters generated by Derive4J (all data constructions must go through the smart constructors).
To support this idiom, we need a Visbility.Smart
(better name?) that will only expose getters and pattern matching as public
(rather, 'same' visibiliy) and generate constructors and setters method with visbility 'package'.
So that they won't clash with method reexported as public.
Address #44 (comment)
This idea is to support the following pattern matching scheme:
Request r = ...
int bodySize = Requests.caseOf(r)
.GET(path -> 0)
.DELETE(path -> 0)
.PUT((path, body) -> body.length())
.POST((path, body) -> body.length())
The README says:
Derive4J philosophy is to be as safe and consistent as possible. That is why Object.{equals, hashCode, toString} are not implemented by generated classes by default. Nonetheless, as a concession to legacy.
If you have a minute, can you please clarify what this means? Specifically, why is not having these methods "safe and consistent".
Thank you!
Make them so might be hard...
http://stackoverflow.com/questions/17503131/how-to-make-catamorphisms-work-with-parameterized-indexed-types
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.