Algebraic Data Type Code Generator for Java and Apache Maven
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
datatyper-example
datatyper-lib
datatyper-maven-plugin
datatyper-maven-tile
.gitignore
.travis.yml
README.adoc
pom.xml

README.adoc

Java ADT Generator

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.

Example DataTyper File

Request.typer
-- 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 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()

Matching values

Total Match Coverage via Visitor

An inner Matcher interface is also generated, containing a separate match method for each unique datatype case:

public interface Matcher<Return> {
  Return match(Request.GET GET);
  Return match(Request.DELETE DELETE);
  Return match(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 match(Request.GET GET) {
    return GET.path().length();
  }

  @Override
  public Integer match(Request.DELETE DELETE) {
    return DELETE.path().length();
  }

  @Override
  public Integer match(Request.POST POST) {
    return POST.path().length();
  }
});

Fluent Matchers

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.

Supporting Super-Type marker interfaces

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 jparsec parser library to terminate the CSV list without failing the parse. This is being tracked as issue #8.

Compile time dependencies

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.

Configuring Your Maven Build

Standard Usage

pom.xml
<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>

Maven Tiles Usage

<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>