datatyper
datatyper copied to clipboard
Algebraic Data Type Code Generator for Java and Apache Maven
Java ADT Generator
:toc: :toc-placement: preamble
Algebraic Data-Types for Java.
datatyper-maven-plugin is an http://maven.apache.org[Apache Maven] plugin to generate immutable, algebraic data types for use in JDK 8 based projects.
Example DataTyper File
.Request.typer [source,haskell]
-- 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);
Generated Classes
- A top level
abstractclass 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()
Matching values
Total Match Coverage via Visitor
An inner Matcher interface is also generated, containing a separate match method for each unique datatype case:
[source,java]
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:
[source,java]
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(); } });
Fluent Matchers
DataTyper supports an alternate matching style based on a fluent lambda syntax:
[source,java]
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.
Accepting Values
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:
[source,java]
public interface Accepting<Return> { void GET(Request.GET GET); void DELETE(Request.DELETE DELETE); void POST(Request.POST POST); ... }
and is used:
[source,java]
Request.Matching<Integer> matched = req.accepting() .DELETE( delete -> handleDeleting(delete.path()) ) .orElse(req -> throw UnsupportedOperation("lol wut?"));
Supporting Super-Type marker interfaces
[source,java]
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 ).
[NOTE]
I really don't like the ()) style syntax, but as yet I'm not sure yet how to get the https://github.com/jparsec/jparsec[jparsec] parser library to terminate the CSV list without failing the parse. This is being tracked as https://github.com/talios/datatyper/issues/8[issue #8].
Compile time dependencies
The code generated by datatyper-maven-plugin uses the https://github.com/google/auto/tree/master/value[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.