Algebraic Data-Types for Java.
datatyper-maven-plugin
is an Apache Maven plugin to generate immutable, algebraic data types for use in JDK 8 based projects.
-- A simple model for an HTTP based API
package com.theoryinpractise.typer.examples;
-- By not specifying the return type with "->" for a given case,
-- the inferred return type should just be the individual case.
data Request implements (com.theoryinpractise.typer.Shouter)
= GET (path : String)
| DELETE (path : String)
| POST (path : String, body : String);
-- By not specifying any arguments for a data type, it is generated as a
-- singleton instance which could be used in place of standard enums.
data Day
= Monday | Tuesday | Wednesday
| Thursday | Friday | Saturday | Sunday;
-- java.lang is "imported" by default, but if you want to use other classes,
-- simply import them
import java.util.Date;
data Log
= info(date: Date, message: String)
| debug(date: Date, message: String)
| warn(date: Date, message: String)
| error(date: Date, message: String);
-
A top level
abstract
class acting as a module for the datatype. -
Separate inner classes and constructor functions for each alterative type.
-
A matcher interface providing total coverage for each alternative type.
-
A fluent matching class for simple, inline matching.
A top-level abstract
class is generated for each datatype, with additional abstract
subclasses for each individual case, these classes are annotatd with @AutoValue
and will generate concrete, immutable, private instance classes via the Google Autovalue library.
Static methods on the top level class are provided to construct instances of your dataypes.
The constructor methods return their concrete type, allowing local code to easily access the types members.
Request.GET("/api/story/32").path()
An inner Matcher
interface is also generated, containing a separate match
method for each unique datatype case:
public interface Matcher<Return> {
Return GET(Request.GET GET);
Return DELETE(Request.DELETE DELETE);
Return POST(Request.POST POST);
...
}
which is used in conjunction with the instance level match
method defined in the top level abstract class, this is used as such:
int pathLength = req.match(new Request.Matcher() {
@Override
public Integer GET(Request.GET GET) {
return GET.path().length();
}
@Override
public Integer DELETE(Request.DELETE DELETE) {
return DELETE.path().length();
}
@Override
public Integer POST(Request.POST POST) {
return POST.path().length();
}
});
DataTyper supports an alternate matching style based on a fluent lambda syntax:
Request.Matching<Integer> matched = req.matching()
.GET( get -> get.path().length() );
if (matched.isMatched()) {
int length = matched.get();
}
int length = req.<Integer>matching()
.GET( get -> get.path().length() )
.orElse(0);
Optional<Integer> length = req.<Integer>matching()
.GET( get -> get.path().length() )
.find();
The generated Request.Matching
class is somewhat akin to an Optional
value, the only difference being individual type case methods providing a functional style match expression.
There are times you simply want to consume a specific value and don’t require a result, for this we have a Request.Accepting
fluent interface:
public interface Accepting<Return> {
void GET(Request.GET GET);
void DELETE(Request.DELETE DELETE);
void POST(Request.POST POST);
...
}
and is used:
Request.Matching<Integer> matched = req.accepting()
.DELETE( delete -> handleDeleting(delete.path()) )
.orElse(req -> throw UnsupportedOperation("lol wut?"));
data SomeType implements (some.marker.Interface, some.other.Interface)
= Value();
Data Type declarations define a list of Java class names that the base class should implement
. These classes MUST be interfaces, and only contain static
or default
methods ( otherwise the generated code will be fail to compile ).
The code generated by datatyper-maven-plugin
uses the Google Auto-Value annotations to generate it’s immutable classes, so this is required to be listed as a compile
dependency in your maven project.
Note
|
There are no run-time dependencies introduced by the DataTyper project. |
<plugins>
<plugin>
<groupId>com.theoryinpractise.datatyper</groupId>
<artifactId>datatyper-maven-plugin</artifactId>
<version>1.0.1</version>
<executions>
<execution>
<id>datatyper</id>
<goals>
<goal>datatyper</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
...
<dependencies>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>1.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
<plugins>
<plugin>
<groupId>io.repaint.maven</groupId>
<artifactId>tiles-maven-plugin</artifactId>
<version>2.10</version>
<extensions>true</extensions>
<configuration>
<tiles>
<tile>com.theoryinpractise.datatyper:datatyper-maven-tile:[1.0.0,2.0.0)</tile>
</tiles>
</configuration>
</plugin>
</plugins>