Derive4J - Auto derivation for Functional Java

Derivation of Functional-Java optics and typeclasses via Derive4J API

License

License

The GNU General Public License
Categories

Categories

derive4j General Purpose Libraries Functional Programming
GroupId

GroupId

org.derive4j
ArtifactId

ArtifactId

derive4j-fj
Last Version

Last Version

0.2
Release Date

Release Date

Type

Type

jar
Description

Description

Derive4J - Auto derivation for Functional Java
Derivation of Functional-Java optics and typeclasses via Derive4J API
Project URL

Project URL

https://github.com/derive4j/derive4j-fj
Project Organization

Project Organization

Derive4J contributors

Download derive4j-fj

How to add to project

<!-- https://jarcasting.com/artifacts/org.derive4j/derive4j-fj/ -->
<dependency>
    <groupId>org.derive4j</groupId>
    <artifactId>derive4j-fj</artifactId>
    <version>0.2</version>
</dependency>
// https://jarcasting.com/artifacts/org.derive4j/derive4j-fj/
implementation 'org.derive4j:derive4j-fj:0.2'
// https://jarcasting.com/artifacts/org.derive4j/derive4j-fj/
implementation ("org.derive4j:derive4j-fj:0.2")
'org.derive4j:derive4j-fj:jar:0.2'
<dependency org="org.derive4j" name="derive4j-fj" rev="0.2">
  <artifact name="derive4j-fj" type="jar" />
</dependency>
@Grapes(
@Grab(group='org.derive4j', module='derive4j-fj', version='0.2')
)
libraryDependencies += "org.derive4j" % "derive4j-fj" % "0.2"
[org.derive4j/derive4j-fj "0.2"]

Dependencies

There are no dependencies for this project. It is a standalone project that does not depend on any other jars.

Project Modules

There are no modules declared in this project.

FunctionalJava on Derive4J steroids!

This Derive4J extension permits automatic derivation of FunctionalJava instances for the following type classes:

(generation of optics is planned).

So, now, no excuse to write unsafe code that breaks parametricity! ;)

"Implicit" resolution of instances

The implementation of a type class instance may often depends on the instances of the data type fields. Derive4J will try its best to resolve those dependencies for you, following rules similar to scala implicit resolution. In some cases, Derive4J can fail to find a valid instance for a field, for one of the following two reasons:

  • because it does not exist (in the expected classes, ie. its an "orphan instance")
  • or because the instance for that field is not yet generated AND the field type is parametrized.

In both cases you will have to provide a static field or method (in the class of the ADT under derivation) that returns the missing type class instance. The implementation can simply be a forward to a generated instance (for the second case).

Example Usage:

import fj.*;
import org.derive4j.*;

@Data(flavour = Flavour.FJ)
@Derive(@Instances({ Show.class, Hash.class, Equal.class, Ord.class}))
public abstract class Either<A, B> {

  /**
   * The catamorphism for either. Folds over this either breaking into left or right.
   *
   * @param left The function to call if this is left.
   * @param right The function to call if this is right.
   * @return The reduced value.
   */
  public abstract <X> X either(F<A, X> left, F<B, X> right);


  // In case you need to interact with unsafe code that
  // expects hashCode/equal/toString to be implemented:

  @Deprecated
  @Override
  public final boolean equals(Object obj) {
    return Equal.equals0(Either.class, this, obj,
        Eithers.eitherEqual(Equal.anyEqual(), Equal.anyEqual()));
  }

  @Deprecated
  @Override
  public final int hashCode() {
    return Eithers.<A, B>eitherHash(Hash.anyHash(), Hash.anyHash()).hash(this);
  }

  @Deprecated
  @Override
  public final String toString() {
    return Eithers.<A, B>eitherShow(Show.anyShow(), Show.anyShow()).showS(this);
  }
}

Derive4J, through this extension will then derive the following code in the generated Eithers.java file:

  
  public static <A, B> Show<Either<A, B>> eitherShow(Show<A> aShow, Show<B> bShow) {
    return Show.show(either -> either.either(
      (left) -> Stream.fromString("left(").append(() -> aShow.show(left)).append(Stream.fromString(")")),
      (right) -> Stream.fromString("right(").append(() -> bShow.show(right)).append(Stream.fromString(")"))
    ));
  }

  public static <A, B> Ord<Either<A, B>> eitherOrd(Ord<A> aOrd, Ord<B> bOrd) {
    return Ord.ordDef(either1 -> either1.either(
      (left1) -> either2 -> either2.either(
        (left2) -> {
          Ordering o = Ordering.EQ;
          o = aOrd.compare(left1, left2);
          if (o != Ordering.EQ) return o;
          return o;
        },
        (right2) -> Ordering.LT
      ),
      (right1) -> either2 -> either2.either(
        (left2) -> Ordering.GT,
        (right2) -> {
          Ordering o = Ordering.EQ;
          o = bOrd.compare(right1, right2);
          if (o != Ordering.EQ) return o;
          return o;
        }
      )
    ));
  }

  public static <A, B> Equal<Either<A, B>> eitherEqual(Equal<A> aEqual, Equal<B> bEqual) {
    return Equal.equalDef(either1 -> either1.either(
      (left1) -> either2 -> either2.either(
        (left2) -> aEqual.eq(left1, left2),
        (right2) -> false
      ),
      (right1) -> either2 -> either2.either(
        (left2) -> false,
        (right2) -> bEqual.eq(right1, right2)
      )
    ));
  }

  public static <A, B> Hash<Either<A, B>> eitherHash(Hash<A> aHash, Hash<B> bHash) {
    return Hash.hash(either -> either.either(
      (left) -> 23 + aHash.hash(left),
      (right) -> 29 + bHash.hash(right)
    ));
}

Use it in your project

Derive4J-FJ is a "plugin" of Derive4J and should be declared (as well) as a apt/compile-time only dependency (not needed at runtime). So while derive4j and derive4j-fj are (L)GPL-licensed, the generated code is not linked to derive4j, and thus derive4j can be used in any project (proprietary or not). Also you will need jdk8 version of FunctionalJava artifacts (4.7+).

Maven:

<dependency>
    <groupId>org.functionaljava</groupId>
    <artifactId>functionaljava_1.8</artifactId>
    <version>4.7</version>
</dependency>
<dependency>
  <groupId>org.derive4j</groupId>
  <artifactId>derive4j-fj</artifactId>
  <version>0.2</version>
  <optional>true</optional>
</dependency>
<dependency>
  <groupId>org.derive4j</groupId>
  <artifactId>derive4j</artifactId>
  <version>0.12.4</version>
  <optional>true</optional>
</dependency>

Gradle

compile "org.functionaljava:functionaljava_1.8:4.7"
compile(group: 'org.derive4j', name: 'derive4j', version: '0.12.4', ext: 'jar')
compile(group: 'org.derive4j', name: 'derive4j-fj', version: '0.2', ext: 'jar')

or better using the gradle-apt-plugin:

compile "org.functionaljava:functionaljava_1.8:4.7"
compileOnly "org.derive4j:derive4j-annotation:0.12.4"
apt "org.derive4j:derive4j:0.12.4"
apt "org.derive4j:derive4j-fj:0.2"

Contributing

Bug reports, feature requests and pull requests are welcome, as well as contributions to improve documentation.

Contact

[email protected], @jb9i or use the project GitHub issues.

org.derive4j

Versions

Version
0.2
0.1