Code Monkey home page Code Monkey logo

hkt's Introduction

Higher Kinded Type machinery for Java

Gitter Maven Central Travis

This project provides type-safety for the higher kinded type encoding demonstrated in https://github.com/highj/highj via a JSR269 annotation processor.

For some theorical explanation of the encoding you may refer to the Lightweight higher-kinded polymorphism paper.

Usage

Choose your HK encoding:

the two basic possibilities are:

class HkTest<A> implements __<HkTest<?>, A> {...}

and

class HkTest<A> implements __<HkTest.w, A> {
  enum w { // could be any name, also could be a static nested class.
  }
}

We say that __<HkTest.w, A> is the HK encoding of HkTest and call w the witness type of HkTest.

What about binary type constructors ? Ternary ? And more ?

@derive4j/hkt supplies interfaces __<f, A>, __2<f, A, B> up to __9<f, A, B, C, D, E, F, G, H, I>.

For example, a disjoint union type commonly called "Either" could be declared this way :

class Either<A, B> implements __2<Either.µ, A, B> {
  enum µ {}
  ...
}

Obligatory Monad example:

The higher kinded polymorphism gained by the encoding allows us to express things that are normally inexpressible in Java. Eg.:

public interface Monad<m> {
  <A> __<m, A> pure(A a);

  <A, B> __<m, B> bind(__<m, A> ma, Function<A, __<m, B>> f);

  default <A, B> __<m, B> map(__<m, A> ma, Function<A, B> f) {
    return bind(ma, f.andThen(this::pure));
  }
}

Aliases interfaces

You may want to create aliases of derive4j hkt __* interfaces that better suit your naming preferences, maybe also adding some default methods. Eg.:

interface HigherKind1<TC extends HigherKind1<TC, ?>, T> extends __<TC, T> {
  default <R> R transform(Function<__<TC, T>, R> f) {
    return f.apply(this);
  }
}

And so your hk-encoded classes would look like:

class HkTest<A> implements HigherKind1<HkTest<?>, A> {...}

In any case, just try: if you do something wrong the annotation processor shall help you!

A note on safety : do not cast! Use the generated safe cast methods

By default the annotation processor will generate a Hkt class in each package that contains hk-encoded classes.

The generated class contains casting methods and factories of TypeEq that allow you to safely recover the original type from its hk-encoding.

Here is an example :

  • given the HKT types
class Maybe<A> implements __<Maybe.µ, A> {...}

and

class List<A> implements __<List.µ, A> {...}

both in package myorg.data

  • then the following class will be generated
package myorg.data;

final class Hkt {
  private Hkt() {}
  
  static <A> Maybe<A> asMaybe(final __<Maybe.µ, A> hkt) {
    return (Maybe<A>) hkt;
  }
  
  static <A> List<A> asList(final __<List.µ, A> hkt) {
    return (List<A>) hkt;
  }
}

Now you may ask : why is that safe ? I could implement __<Maybe.µ, A> in my Foo<A> class, pass an instance of it to Hkt.asMaybe and then boom !

And to this the answer is no, you can't. That's the whole point of the hkt processor : would you try to implement __<Maybe.µ, A> in any other class than Maybe, you'd get a compile time error.

The processor thus ensures that the only possible implementation of __<Maybe.µ, A> is Maybe<A> : hence the safety of the cast in the generated methods.

Configuration of code generation

Code generation can be customized by using the HktConfig annotation (on package-info or classes).

Consider the example of the previous section : we would like the generated methods to be called toX instead of asX. Easy ! Just declare, in the myorg.data package, a package-info file as such :

@HktConfig(coerceMethodName = "to{ClassName}")
package myorg.data;

Note that configuration is handled hierarchically through packages, classes and inner classes. That means that would you want to keep your toX methods and at the same time have the one for List generated in its own class, you could declare a package-info as afore mentionned and then annotate the List class this way:

@HktConfig(generateIn = "MyHktList")
class List<A> implements __<List.µ, A> {...}

As expected, the following two files would then be generated :

package myorg.data;

final class Hkt {
  private Hkt() {}
  
  static <A> Maybe<A> toMaybe(final __<Maybe.µ, A> hkt) {
    return (Maybe<A>) hkt;
  }
}

and

package myorg.data;

final class MyHktList {
  private MyHktList() {}
  
  static <A> List<A> toList(final __<List.µ, A> hkt) {
    return (List<A>) hkt;
  }
}

I want it !

Maven

<dependency>
  <groupId>org.derive4j.hkt</groupId>
  <artifactId>hkt</artifactId>
  <version>0.9.2</version>
</dependency>

Gradle

compile(group: 'org.derive4j.hkt', name: 'hkt', version: '0.9.2', ext: 'jar')

hkt's People

Contributors

danielgronau avatar gitter-badger avatar gneuvill avatar jbgi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hkt's Issues

[Question] Strategy for using HKT with classes we can not touch.

Not sure if this is the right place for this question.

But is there a way to easily handle using HKT for classes we can not extend/implement __, __2, etc. for. Without writing a bunch of wrapper classes.

For example, using HKT with Java 8's Stream class.

generation of narrow method

We should check that a static 'narrow' method with correct signature (and implementation if possible) is present in the class (unless annotated @Data).
The method could have another name. (or should it be normalized ?)

Write README

Including a presentation of use-cases, links to prior art and papers, projects that use this lib.

Fallback on a runtime check

Since an annotation processor can be deactivated at compile time, it seems desirable to have at our disposal a fallback mechanism that would apply the very same logic as the one of the processor at runtime ; ideally, and in java terms, this should even happen at class loading time.

This would necessitates :

  • to abstract over the underlying api (javax.lang.model or java.lang.reflect)
  • to automatically activate some code at class loading time : I've currently no idea how to do that (if that is even possible...)

Strange errors in IntelliJ (Possible collision / interference with Lombok)

Hey guys,

I'd like to turn on this feature in cyclops-react (aol/cyclops#347), when I added the hkt project as a dependency and make cyclops-react Higher class extend org.derive4j.hkt.__ everything compiles just fine from the command line. Unfortunately, IntelliJ starts showing some strange errors instead.

Lombok's val will generally fail, but other errors seem unrelated and really strange. I've attached a screenshot where the line numbers of some spurious compile errors have been misattributed.

I'm not sure how easy or hard it would be to fix this (as the issue may well be on the IntelliJ side or Lomboks), but I thought you'd appreciate getting a report of it.

Thanks,
John

screen shot 2017-05-16 at 22 02 24

Annotation processor incorrectly ignore implementation of __ alias interfaces

release 0.9 ignore HK encoded classe that inherit from derive4j hkt interface through an alias interface.
Eg. nothing is generated for:

class HkTest<A> implements HigherKind1<HkTest<?>, A> {}

interface HigherKind1<TC extends HigherKind1<TC, ?>, T> extends __<TC, T> {}

(was testing how the encoding could be used in javaslang)

HigherKind machinery à la highj with associated annotation processor

This means the _ classes at the root of https://github.com/DanielGronau/highj/tree/master/src
Should be renamed to __ or other name (hk?) to avoid javac warning.

The annotation processor should check what @gneuvill described in functionaljava/functionaljava#126 (comment) to ensure acceptable type-safety.

Additionally it would also check that a narrow/coherce method exists with the right type signature.

@gneuvill Would you be up to the task?

Rename Leibniz to TypeEq

Leibniz is probably unnecessarily non-intuitive. In type theory this concept is usually more often refereed as "propositional equality" or "type equality" (as in "propositions as types").

I took the name from scalaz but thinking of it, the term "Leibniz equality" is maybe best left in the javadoc.

Also this would be more in line with Haskell built-in type equality (also it may be worth having a look at the combinators provided by this package).

@clinuxrulz, @gneuvill, WDYT? (originally, @clinuxrulz, I think you also used TypeEq).

Alternative could be TypeEquals (used in purescript).

Encoding of generated files should be UTF-8

Under Widows 10, I have the problem that the generated java files have the Windows default encoding, not UTF-8, which garbles the µ identifier in my case.

After trying every way to set an encoding in Maven or my IDE, I assume the annotation processor itself must take care of it. I think the relevant code location is GenCode:146:

Filer.createSourceFile(genClassName).openWriter()

As far as I understand, openWriter() simply wraps the underlying output stream with am OutputStreamWriter without setting an encoding, which results in using the system default (and not the encoding set for javac etc). I haven't tested it, but I would assume that using something like

new OutputStreamWriter(Filer.createSourceFile(genClassName).openOutputStream(), "UTF8")

could probably solve the problem. Of course instead of hard-coding, this could be also a parameter of the annotation processor (although I'm not sure in which scenarios would require another encoding)

Support for types where some type parameters actually refer to mu-interface type parameters

Consider the Compose type from https://hackage.haskell.org/package/transformers-0.4.3.0/docs/Data-Functor-Compose.html

It is defined as newtype Compose f g a = Compose { getCompose :: f (g a) }, and allows to treat nested types like "List of Maybe" as a single HKT.

I tried to translate it straight-forward to:

package org.highj.data.structural;

import org.derive4j.hkt.__;

public class Compose<F,G,A> implements __<Compose.µ<F,G>,A> {

    public interface µ<F,G>{}

    public Compose(__<F, __<G, A>> value) {
        this.value = value;
    }

    private final __<F,__<G,A>> value;

    public __<F, __<G, A>> get() {
        return value;
    }

}

However, HTK complains:
Error:(5, 8) java: org.derive4j.hkt.__ is not the correct interface to use. Given the number of type parameters, org.highj.data.structural.Compose should implements org.derive4j.hkt.__3<org.highj.data.structural.Compose.µ<F,G>, F, G, A>.

Maybe I overlooked something, or there is another way to implement it, but I got stuck.

Support for higher kinded types with arbitrary type arguments.

Let's assume we offer interfaces up to __n (currently 5, soon 9).
There will be a day when someone would need to create a class with n + x type parameters (x >= 1).
We cannot add new interface infinitely, so there must be a way to cope with this situation.
My take would be that if a class has more that 9 type parameters then the higher kinded interface will only expose the last 9 type parameters, and the first "non hkt" type parameters would need to be backed in the type constructor witness in one of the two following way:

class WildType<A, B, C, D, E, F> imlements __5<µ<A>, B, C, D, E, F> {
    public static final class µ<A> {}
}
class WildType<A, B, C, D, E, F> imlements __5<__<µ, A>, B, C, D, E, F> {
    public static final class µ {}
}

The later encoding is more consistent with the hierarchy and eventually allow to abstract over all type parameters, but the first one may be simpler for types with lots of type parameters.

Pb with types having more than 9 parameters

public interface Try8<A, B, C, D, E, F, G, H, I, Z extends Exception>
        extends __<__9<Try8.µ, A, B, C, D, E, F, G, H, I>, Z> {
    enum µ {}
    
    I f(A a, B b, C c, D d, E e, F f, G g, H h) throws Z;
}

This doesn't work. Got error: org.derive4j.hkt.__ is not the correct interface to use. Given the number of type parameters, fj.function.Try8 should extends org.derive4j.hkt.__10<µ, A, B, C, D, E, F, G, H, I, Z>.

Obviously we don't have __10, but I thought the curried encoding would work...

Related to #14.

Ecj support

for now, this means disabling openjdk specific inspections when the api is not available,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.