Hi Jean-Baptiste,
Beau boulot ! I've tried it out and I like it very much. As a matter of
fact, I've been thinking about making something like this for quite a
while since I read
this very nice article (that you could add to your README by
the way) ; alas, I never found the time and energy to actually do it so
I'm most grateful you did !
I still have a few remarks to make about your work, if you don't mind :
- the first one is minor, but I didn't understand why the pattern-match
syntax returns a function. For instance, following up the 'Request'
example, the 'getPath' method is implemented that way :
public static String getPath(Request request) {
return Requests.cases()
.GET(path -> path)
.DELETE(path -> path)
.PUT((path, body) -> path)
.POST((path, body) -> path)
.apply(request);
}
However, given the 'match' method is defined on 'request', something
like the following would have seemed more obvious to me :
public static String getPath(Request request) {
return reques.match(Requests.cases()
.GET(path -> path)
.DELETE(path -> path)
.PUT((path, body) -> path)
.POST((path, body) -> path));
}
But again, this is just a detail.
- the second one is much more important in my opinion : indeed, your
claim that the pattern-matching machinery generated by derive4j is
exhaustive is unfortunately untrue : in the 'Request' example, anyone
anywhere could extend the Request class, addind further cases to the
original algebra in an unconstrainable way ! Something like the
'sealed' keyword in Scala is missing. But, as you most certainly
know, the functionaljava guys discovered a way to emulate this in
Java : just declare a private default constructor in your abstract
class. I thus propose the following modifications to be made to
derive4j :
* require that the 'template' class name is suffixed by '_ADT'
ex :
@Data
class Request_ADT {...}
* if possible, require that this class is declared final (compile
time error otherwise)
* in the 'Requests' file generated by derive4j, declare the actual
basis of the ADT
ex :
public static abstract class Request {
private Request() {} // sealed !
private static final class GET extends Request {...}
private static final class DELETE extends Request {...}
etc...
}
The average Java Joe - that I once was - is then conveniently
fucked : what can he possibly do with his brand new 'Request'
type ? Just use the available constructors and pattern match on
them, but certainly not extend it in some bizarroid and nasty
way...
- the third one is a little caprice : would it be possible to
benefit from some curried version of the '*Matcher' SAM
types alongside the existing ones ?
So that we can write :
public static String getPath(Request request) {
return reques.match(Requests.cases()
.GET(path -> path)
.DELETE(path -> path)
.PUT(path -> body -> path) // curried F2
.POST(path -> body -> path));
}
Okay, that's it ! Sorry for this much too long post. I hope that most of
it makes sense and look forward for your opinion about my propositions.
Thanks again for that nice piece of work,
Grégoire.