├── .gitignore ├── src └── main │ ├── java │ └── com │ │ └── github │ │ └── sviperll │ │ └── higherkindedjava │ │ ├── Functor.java │ │ ├── Monad.java │ │ ├── GADTVisitor.java │ │ ├── GenerateTypeConstructor.java │ │ ├── data │ │ ├── Optional.java │ │ ├── List.java │ │ ├── EmptyOptional.java │ │ ├── EmptyList.java │ │ ├── PresentOptional.java │ │ ├── ListMonad.java │ │ ├── PrependedList.java │ │ ├── OptionalMonad.java │ │ ├── AnyOptional.java │ │ └── AnyList.java │ │ ├── Main.java │ │ ├── Type.java │ │ └── GADT.java │ └── resources │ └── com │ └── github │ └── sviperll │ └── higherkindedjava │ └── TypeConstructor.mustache ├── nbactions.xml ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *~ 3 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/Functor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava; 7 | 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 12 | * @author vir 13 | * @param 14 | * @param 15 | */ 16 | public interface Functor { 17 | Type.App map(Type.App self, Function f); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/Monad.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava; 7 | 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 12 | * @author vir 13 | */ 14 | public interface Monad extends Functor { 15 | Type.App unit(T value); 16 | 17 | default Type.App join(Type.App> values) { 18 | return flatMap(values, Function.identity()); 19 | } 20 | 21 | default Type.App flatMap(Type.App self, Function> f) { 22 | return join(map(self, f)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/GADTVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | package com.github.sviperll.higherkindedjava; 8 | 9 | import java.util.function.Function; 10 | 11 | /** 12 | * 13 | * @author Victor Nazarov <asviraspossible@gmail.com> 14 | */ 15 | public interface GADTVisitor { 16 | R zero(Type.Eq tArgKnowledge); 17 | R succ(GADT value, Type.Eq tArgKnowledge); 18 | R isZero(GADT value, Type.Eq tArgKnowledge); 19 | R ifClause(GADT condition, GADT thenValue, GADT elseValue); 20 | R lam(Function> function, Type.Eq> tArgKnowledge); 21 | R app(GADT> function, GADT argument); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/GenerateTypeConstructor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | package com.github.sviperll.higherkindedjava; 8 | 9 | import com.github.sviperll.writejava4me.GeneratesClass; 10 | import java.lang.annotation.Documented; 11 | import java.lang.annotation.ElementType; 12 | import java.lang.annotation.Retention; 13 | import java.lang.annotation.RetentionPolicy; 14 | import java.lang.annotation.Target; 15 | /** 16 | * 17 | * @author Victor Nazarov <asviraspossible@gmail.com> 18 | */ 19 | @Retention(RetentionPolicy.SOURCE) 20 | @Target(ElementType.TYPE) 21 | @Documented 22 | @GeneratesClass(classNameTemplateString = "{{annotated}}TypeConstructor", classTemplateResourcePath = "com/github/sviperll/higherkindedjava/TypeConstructor.mustache") 23 | public @interface GenerateTypeConstructor { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/Optional.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import com.github.sviperll.higherkindedjava.GenerateTypeConstructor; 9 | 10 | /** 11 | * 12 | * @author vir 13 | */ 14 | @GenerateTypeConstructor 15 | public interface Optional { 16 | public static EmptyOptional empty() { 17 | return new EmptyOptional(); 18 | } 19 | public static PresentOptional unit(T value) { 20 | return new PresentOptional<>(value); 21 | } 22 | public static PresentOptional present(T value) { 23 | return new PresentOptional<>(value); 24 | } 25 | public static AnyOptional join(Optional> values) { 26 | return values.toAny().flatMap(list -> list); 27 | } 28 | 29 | AnyOptional toAny(); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/List.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import com.github.sviperll.higherkindedjava.GenerateTypeConstructor; 9 | 10 | /** 11 | * 12 | * @author vir 13 | * @param 14 | */ 15 | @GenerateTypeConstructor 16 | public interface List { 17 | public static PrependedList unit(T value) { 18 | return new PrependedList<>(value, empty()); 19 | } 20 | public static PrependedList prepend(T head, List tail) { 21 | return new PrependedList<>(head, tail); 22 | } 23 | public static EmptyList empty() { 24 | return new EmptyList<>(); 25 | } 26 | public static AnyList join(List> values) { 27 | return values.toAny().flatMap(list -> list); 28 | } 29 | 30 | AnyList toAny(); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/com/github/sviperll/higherkindedjava/TypeConstructor.mustache: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | package {{package}}; 8 | 9 | import com.github.sviperll.higherkindedjava.Type; 10 | 11 | public class {{class}} { 12 | public static final Is GET = new Is<>(Type.ConstructorIs.SUPPORT); 13 | {{class}}() { 14 | } 15 | public static class Is extends Type.ConstructorIs { 16 | private Is(Type.ConstructorIs.Support support) { 17 | super(support); 18 | } 19 | public Type.App convertToTypeApp({{annotated}} value) { 20 | return this.<{{annotated}}, T>unsafeConvertToTypeApp(value); 21 | } 22 | public {{annotated}} convertTo{{annotated}}(Type.App value) { 23 | return this.<{{annotated}}, T>unsafeConvertToValue(value); 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/EmptyOptional.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 12 | * @author vir 13 | * @param 14 | */ 15 | public class EmptyOptional implements Optional { 16 | private AnyOptional any = null; 17 | 18 | public EmptyOptional map(Function f) { 19 | return new EmptyOptional<>(); 20 | } 21 | public EmptyOptional flatMap(Function> f) { 22 | return new EmptyOptional<>(); 23 | } 24 | 25 | @Override 26 | public AnyOptional toAny() { 27 | if (any == null) 28 | any = AnyOptional.wrap(new InitializationToken()); 29 | return any; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "EmptyOptional{" + '}'; 35 | } 36 | 37 | class InitializationToken { 38 | private InitializationToken() { 39 | } 40 | EmptyOptional instance() { 41 | return EmptyOptional.this; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/EmptyList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 12 | * @author vir 13 | * @param 14 | */ 15 | public class EmptyList implements List { 16 | private AnyList any = null; 17 | 18 | public EmptyList() { 19 | } 20 | 21 | public EmptyList map(Function f) { 22 | return new EmptyList<>(); 23 | } 24 | public EmptyList flatMap(Function> f) { 25 | return new EmptyList<>(); 26 | } 27 | 28 | @Override 29 | public AnyList toAny() { 30 | if (any == null) { 31 | any = AnyList.wrap(new InitializationToken()); 32 | } 33 | return any; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "EmptyList{" + '}'; 39 | } 40 | 41 | class InitializationToken { 42 | private InitializationToken() { 43 | } 44 | public EmptyList instance() { 45 | return EmptyList.this; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/PresentOptional.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 12 | * @author vir 13 | */ 14 | public class PresentOptional implements Optional { 15 | private AnyOptional any = null; 16 | private final T value; 17 | public PresentOptional(T value) { 18 | this.value = value; 19 | } 20 | 21 | public T value() { 22 | return value; 23 | } 24 | 25 | public PresentOptional map(Function f) { 26 | return new PresentOptional<>(f.apply(value)); 27 | } 28 | public AnyOptional flatMap(Function> f) { 29 | return f.apply(value).toAny(); 30 | } 31 | 32 | @Override 33 | public AnyOptional toAny() { 34 | if (any == null) 35 | any = AnyOptional.wrap(new InitializationToken()); 36 | return any; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "PresentOptional{" + "value=" + value + '}'; 42 | } 43 | 44 | class InitializationToken { 45 | private InitializationToken() { 46 | } 47 | PresentOptional instance() { 48 | return PresentOptional.this; 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/ListMonad.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import com.github.sviperll.higherkindedjava.Type; 9 | import java.util.function.Function; 10 | 11 | /** 12 | * 13 | * @author vir 14 | */ 15 | public class ListMonad implements com.github.sviperll.higherkindedjava.Monad { 16 | public static final ListMonad INSTANCE = new ListMonad<>(ListTypeConstructor.GET); 17 | private final ListTypeConstructor.Is tyConstrKnowledge; 18 | public ListMonad(ListTypeConstructor.Is tyConstrKnowledge) { 19 | this.tyConstrKnowledge = tyConstrKnowledge; 20 | } 21 | public ListTypeConstructor.Is typeConstructorKnowledge() { 22 | return tyConstrKnowledge; 23 | } 24 | 25 | @Override 26 | public Type.App map(Type.App self, Function f) { 27 | return tyConstrKnowledge.convertToTypeApp(tyConstrKnowledge.convertToList(self).toAny().map(f)); 28 | } 29 | 30 | @Override 31 | public Type.App unit(T value) { 32 | return tyConstrKnowledge.convertToTypeApp(List.unit(value)); 33 | } 34 | 35 | @Override 36 | public Type.App flatMap(Type.App self, Function> f) { 37 | return tyConstrKnowledge.convertToTypeApp(tyConstrKnowledge.convertToList(self).toAny().flatMap(e -> tyConstrKnowledge.convertToList(f.apply(e)))); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/PrependedList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 12 | * @author vir 13 | */ 14 | public class PrependedList implements List { 15 | 16 | private AnyList any = null; 17 | private final T head; 18 | private final AnyList tail; 19 | 20 | public PrependedList(T head, List tail) { 21 | this.head = head; 22 | this.tail = tail.toAny(); 23 | } 24 | 25 | public PrependedList map(Function f) { 26 | return new PrependedList<>(f.apply(head), tail.map(f)); 27 | } 28 | public AnyList flatMap(Function> f) { 29 | return f.apply(head).toAny().append(tail.flatMap(f)); 30 | } 31 | 32 | public T head() { 33 | return head; 34 | } 35 | 36 | public AnyList tail() { 37 | return tail; 38 | } 39 | 40 | @Override 41 | public AnyList toAny() { 42 | if (any == null) 43 | any = AnyList.wrap(new InitializationToken()); 44 | return any; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "PrependedList{" + "head=" + head + ", tail=" + tail + '}'; 50 | } 51 | 52 | class InitializationToken { 53 | private InitializationToken() { 54 | } 55 | public PrependedList instance() { 56 | return PrependedList.this; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/OptionalMonad.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import com.github.sviperll.higherkindedjava.Type; 9 | import java.util.function.Function; 10 | 11 | /** 12 | * 13 | * @author vir 14 | */ 15 | public class OptionalMonad implements com.github.sviperll.higherkindedjava.Monad { 16 | public static final OptionalMonad INSTANCE = new OptionalMonad<>(OptionalTypeConstructor.GET); 17 | private final OptionalTypeConstructor.Is tyConstrKnowledge; 18 | public OptionalMonad(OptionalTypeConstructor.Is tyConstrKnowledge) { 19 | this.tyConstrKnowledge = tyConstrKnowledge; 20 | } 21 | 22 | public OptionalTypeConstructor.Is typeConstructorKnowledge() { 23 | return tyConstrKnowledge; 24 | } 25 | @Override 26 | public Type.App map(Type.App self, Function f) { 27 | return tyConstrKnowledge.convertToTypeApp(tyConstrKnowledge.convertToOptional(self).toAny().map(f)); 28 | } 29 | 30 | @Override 31 | public Type.App unit(T value) { 32 | return tyConstrKnowledge.convertToTypeApp(Optional.present(value)); 33 | } 34 | 35 | @Override 36 | public Type.App flatMap(Type.App self, Function> f) { 37 | return tyConstrKnowledge.convertToTypeApp(tyConstrKnowledge.convertToOptional(self).toAny().flatMap(value -> tyConstrKnowledge.convertToOptional(f.apply(value)))); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath com.github.sviperll.higherkindedjava.Main 14 | java 15 | 16 | 17 | 18 | debug 19 | 20 | jar 21 | 22 | 23 | process-classes 24 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 25 | 26 | 27 | -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath com.github.sviperll.higherkindedjava.Main 28 | java 29 | true 30 | 31 | 32 | 33 | profile 34 | 35 | jar 36 | 37 | 38 | process-classes 39 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 40 | 41 | 42 | -classpath %classpath com.github.sviperll.higherkindedjava.Main 43 | java 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.github.sviperll 5 | higher-kinded-java 6 | 0.1-SNAPSHOT 7 | jar 8 | 9 | UTF-8 10 | 1.8 11 | 1.8 12 | 13 | higher-kinded-java 14 | 15 | 16 | com.github.sviperll 17 | writejava4me 18 | 0.3 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-compiler-plugin 27 | 28 | true 29 | true 30 | 31 | -s 32 | ${project.build.directory}/generated-sources/javac 33 | -Xlint:all 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-antrun-plugin 43 | 1.4 44 | 45 | 46 | generate-sources 47 | 48 | 49 | 50 | 51 | 52 | 53 | run 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/AnyOptional.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 12 | * @author vir 13 | */ 14 | public abstract class AnyOptional implements Optional { 15 | static AnyOptional wrap(EmptyOptional.InitializationToken token) { 16 | EmptyOptional instance = token.instance(); 17 | return new AnyOptional() { 18 | 19 | @Override 20 | public R accept(Visitor visitor) { 21 | return visitor.empty(instance); 22 | } 23 | }; 24 | } 25 | static AnyOptional wrap(PresentOptional.InitializationToken token) { 26 | PresentOptional instance = token.instance(); 27 | return new AnyOptional() { 28 | 29 | @Override 30 | public R accept(Visitor visitor) { 31 | return visitor.present(instance); 32 | } 33 | }; 34 | } 35 | private AnyOptional() { 36 | } 37 | public abstract R accept(Visitor visitor); 38 | 39 | public AnyOptional map(Function f) { 40 | return accept(new Visitor>() { 41 | 42 | @Override 43 | public AnyOptional empty(EmptyOptional empty) { 44 | return empty.map(f).toAny(); 45 | } 46 | 47 | @Override 48 | public AnyOptional present(PresentOptional present) { 49 | return present.map(f).toAny(); 50 | } 51 | }); 52 | } 53 | public AnyOptional flatMap(Function> f) { 54 | return accept(new Visitor>() { 55 | @Override 56 | public AnyOptional empty(EmptyOptional empty) { 57 | return empty.flatMap(f).toAny(); 58 | } 59 | 60 | @Override 61 | public AnyOptional present(PresentOptional present) { 62 | return present.flatMap(f); 63 | } 64 | }); 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return accept(new Visitor() { 70 | 71 | @Override 72 | public String empty(EmptyOptional empty) { 73 | return empty.toString(); 74 | } 75 | 76 | @Override 77 | public String present(PresentOptional present) { 78 | return present.toString(); 79 | } 80 | }); 81 | } 82 | 83 | @Override 84 | public AnyOptional toAny() { 85 | return this; 86 | } 87 | 88 | public interface Visitor { 89 | R empty(EmptyOptional empty); 90 | R present(PresentOptional present); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava; 7 | 8 | import com.github.sviperll.higherkindedjava.data.AnyList; 9 | import com.github.sviperll.higherkindedjava.data.AnyOptional; 10 | import com.github.sviperll.higherkindedjava.data.ListMonad; 11 | import com.github.sviperll.higherkindedjava.data.OptionalMonad; 12 | import com.github.sviperll.higherkindedjava.data.List; 13 | import com.github.sviperll.higherkindedjava.data.ListTypeConstructor; 14 | import com.github.sviperll.higherkindedjava.data.Optional; 15 | import com.github.sviperll.higherkindedjava.data.OptionalTypeConstructor; 16 | import java.util.function.Function; 17 | 18 | /** 19 | * 20 | * @author vir 21 | */ 22 | public class Main { 23 | // Monad generic code 24 | static Type.App transformMonad(Monad monad, Type.App value) { 25 | Type.App result = monad.flatMap(value, i -> monad.unit(5)); 26 | return transformFunctor(monad, result); 27 | } 28 | 29 | // Functor generic code 30 | static Type.App transformFunctor(Functor functor, Type.App value) { 31 | return functor.map(value, i -> i + 1); 32 | } 33 | 34 | // Using specific instance 35 | static AnyList processLists(ListTypeConstructor.Is proof) { 36 | Type.App value = proof.convertToTypeApp(List.prepend(1, List.prepend(2, List.empty()))); 37 | Type.App result = transformMonad(new ListMonad<>(proof), value); 38 | return proof.convertToList(result).toAny(); 39 | } 40 | 41 | // Using specific instance 42 | static AnyOptional processOptionals(OptionalTypeConstructor.Is proof) { 43 | Type.App value = proof.convertToTypeApp(Optional.present(1)); 44 | // Type.App value = Optional.present(1); // Compile-time error 45 | // Type.App value = List.prepend(1, List.empty())); // Compile-time error 46 | // Type.App value = proof.convertToTypeApp(List.prepend(1, List.empty())); // Compile-time error 47 | 48 | Type.App result = transformMonad(new OptionalMonad<>(proof), value); 49 | return proof.convertToOptional(result).toAny(); 50 | } 51 | 52 | public static void main(String[] args) { 53 | System.out.println(processLists(ListTypeConstructor.GET)); 54 | System.out.println(processOptionals(OptionalTypeConstructor.GET)); 55 | 56 | GADT zero = GADT.zero(); 57 | GADT one = GADT.succ(zero); 58 | GADT> f1 = GADT.lam(n -> zero); 59 | GADT> f2 = GADT.lam(n -> one); 60 | GADT exp = GADT.app(GADT.ifClause(GADT.isZero(one), f1, f2), one); 61 | System.out.println(exp + " = " + exp.eval()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/Type.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava; 7 | 8 | import java.util.Objects; 9 | 10 | /** 11 | * 12 | * @author vir 13 | */ 14 | public class Type { 15 | private Type() { 16 | } 17 | 18 | public static class App { 19 | 20 | private final Object value; 21 | private App(V value) { 22 | this.value = value; 23 | } 24 | } 25 | 26 | public static class ConstructorIs { 27 | public static final Support SUPPORT = new Support<>(); 28 | private final Support support; 29 | public ConstructorIs(Support support) { 30 | Objects.requireNonNull(support); 31 | this.support = support; 32 | } 33 | protected Type.App unsafeConvertToTypeApp(V value) { 34 | return support.unsafeConvertToTypeApp(value); 35 | } 36 | protected V unsafeConvertToValue(Type.App value) { 37 | return support.unsafeConvertToValue(value); 38 | } 39 | public static class Support { 40 | private Support() { 41 | } 42 | private Type.App unsafeConvertToTypeApp(V value) { 43 | return new Type.App<>(value); 44 | } 45 | @SuppressWarnings("unchecked") 46 | private V unsafeConvertToValue(Type.App value) { 47 | return (V)value.value; 48 | } 49 | } 50 | } 51 | 52 | /** 53 | * Reified type-equality 54 | */ 55 | public static class Eq { 56 | @SuppressWarnings("rawtypes") 57 | private static final Eq INSTANCE = new Eq(); 58 | 59 | @SuppressWarnings("unchecked") 60 | public static Eq obvious() { 61 | return INSTANCE; 62 | } 63 | 64 | @SuppressWarnings("unchecked") 65 | public static Eq fromTypeApp(Eq, Type.App> equality) { 66 | return INSTANCE; 67 | } 68 | 69 | private Eq() { 70 | } 71 | 72 | @SuppressWarnings("unchecked") 73 | public T cast(U type) { 74 | return (T)type; 75 | } 76 | 77 | @SuppressWarnings("unchecked") 78 | public Eq reverse() { 79 | return INSTANCE; 80 | } 81 | 82 | @SuppressWarnings("unchecked") 83 | public Eq merge(Eq equality) { 84 | return INSTANCE; 85 | } 86 | 87 | @SuppressWarnings("unchecked") 88 | public Eq, Type.App> toTypeApp() { 89 | return INSTANCE; 90 | } 91 | } 92 | public static class Constructor { 93 | private Constructor() { 94 | throw new IllegalStateException("Shouldn't be instantiated"); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/data/AnyList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.sviperll.higherkindedjava.data; 7 | 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 12 | * @author vir 13 | */ 14 | public abstract class AnyList implements List { 15 | public static AnyList wrap(EmptyList.InitializationToken empty) { 16 | EmptyList value = empty.instance(); 17 | return new AnyList() { 18 | @Override 19 | public R accept(Visitor visitor) { 20 | return visitor.empty(value); 21 | } 22 | }; 23 | } 24 | 25 | public static AnyList wrap(PrependedList.InitializationToken prepended) { 26 | PrependedList value = prepended.instance(); 27 | return new AnyList() { 28 | @Override 29 | public R accept(Visitor visitor) { 30 | return visitor.prepended(value); 31 | } 32 | }; 33 | } 34 | 35 | private AnyList() { 36 | } 37 | 38 | public abstract R accept(Visitor visitor); 39 | 40 | @Override 41 | public AnyList toAny() { 42 | return this; 43 | } 44 | 45 | public AnyList map(Function f) { 46 | return accept(new Visitor>() { 47 | @Override 48 | public AnyList empty(EmptyList empty) { 49 | return empty.map(f).toAny(); 50 | } 51 | 52 | @Override 53 | public AnyList prepended(PrependedList prepended) { 54 | return prepended.map(f).toAny(); 55 | } 56 | }); 57 | } 58 | public AnyList flatMap(Function> f) { 59 | return accept(new Visitor>() { 60 | @Override 61 | public AnyList empty(EmptyList empty) { 62 | return empty.flatMap(f).toAny(); 63 | } 64 | 65 | @Override 66 | public AnyList prepended(PrependedList prepended) { 67 | return prepended.flatMap(f).toAny(); 68 | } 69 | }); 70 | } 71 | public AnyList append(List list) { 72 | return accept(new Visitor>() { 73 | @Override 74 | public AnyList empty(EmptyList empty) { 75 | return list.toAny(); 76 | } 77 | 78 | @Override 79 | public AnyList prepended(PrependedList prepended) { 80 | return new PrependedList<>(prepended.head(), prepended.tail().append(list)).toAny(); 81 | } 82 | }); 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | return accept(new Visitor() { 88 | 89 | @Override 90 | public String empty(EmptyList empty) { 91 | return empty.toString(); 92 | } 93 | 94 | @Override 95 | public String prepended(PrependedList prepended) { 96 | return prepended.toString(); 97 | } 98 | }); 99 | } 100 | 101 | 102 | public interface Visitor { 103 | R empty(EmptyList empty); 104 | R prepended(PrependedList prepended); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Higher Kinded Java 2 | ================== 3 | 4 | I've been experimenting with different encodings and I've come up with some new scheme with the ergonomics I mostly like. 5 | It is currently presented in [higher-kinded-java repository](https://github.com/sviperll/higher-kinded-java) 6 | 7 | Basically it's a [highj](https://github.com/highj/highj) encoding, 8 | but with existential type twist. 9 | You can get a taste of using it as a library by looking at the 10 | [Main.java source code](https://github.com/sviperll/higher-kinded-java/blob/master/src/main/java/com/github/sviperll/higherkindedjava/Main.java) 11 | . 12 | 13 | I propose the way to use higher-kinded types based on some annotation processor. 14 | User code should look like this 15 | 16 | ````java 17 | @GenerateTypeConstructor 18 | public interface List { 19 | // Ordinary list definition 20 | } 21 | ```` 22 | 23 | When you use `@GenerateTypeConstructor` annotation, new class is generated. 24 | `ListTypeConstructor` class is generated in above example. 25 | You can use it like this: 26 | 27 | ````java 28 | ListTypeConstructor.Is tyConstrKnowledge = ListTypeConstructor.get; 29 | ```` 30 | 31 | `ListTypeConstructor.Is` object is parametrized by a wildcard-argument. 32 | This is the major difference from highj. 33 | The only way to actually use this object is to capture this wildcard: 34 | 35 | ````java 36 | void playWithListType(ListTypeConstructor.Is tyConstrKnowledge) { 37 | } 38 | 39 | void run() { 40 | ListTypeConstructor.Is tyConstrKnowledge = ListTypeConstructor.get; 41 | playWithListType(tyConstrKnowledge); 42 | } 43 | ```` 44 | 45 | And after you have captured a wildcard you can use `Type.App` objects: 46 | 47 | ````java 48 | void playWithListType(ListTypeConstructor.Is tyConstrKnowledge) { 49 | Type.App typeApp = tyConstrKnowledge.convertToTypeApp(List.of(1, 2, 3)); 50 | List list = tyConstrKnowledge.convertToList(typeApp); 51 | } 52 | 53 | void run() { 54 | ListTypeConstructor.Is tyConstrKnowledge = ListTypeConstructor.get; 55 | playWithListType(tyConstrKnowledge); 56 | } 57 | ```` 58 | 59 | You can't do it without capturing wildcard into some type-variable. 60 | 61 | ````java 62 | void run() { 63 | ListTypeConstructor.Is tyConstrKnowledge = ListTypeConstructor.get; 64 | Type.App typeApp = tyConstrKnowledge.convertToTypeApp(List.of(1, 2, 3)); 65 | 66 | // Compile-time error can't unify two different captured types 67 | List list = tyConstrKnowledge.convertToList(typeApp); 68 | } 69 | ```` 70 | 71 | So, basically this captured type variable is a proof that `Type.App` instance is created by the same 72 | type-constructor-is-object and can be safely transformed back into a List. 73 | And you can't do anything without capturing since `ListTypeConstructor.get` field is actually parametrized with wildcard-type-argument. 74 | 75 | Having this framework at your disposal it's easy to get your 76 | [Monads](https://github.com/sviperll/higher-kinded-java/blob/master/src/main/java/com/github/sviperll/higherkindedjava/Monad.java) 77 | with type-safe implementations ( 78 | [List](https://github.com/sviperll/higher-kinded-java/blob/master/src/main/java/com/github/sviperll/higherkindedjava/data/ListMonad.java) 79 | , 80 | [Optional](https://github.com/sviperll/higher-kinded-java/blob/master/src/main/java/com/github/sviperll/higherkindedjava/data/OptionalMonad.java) 81 | ) 82 | 83 | Rawtypes, manual instantiation of `Type.App` class and plain old casts 84 | can all circumvernt type-safety and cause ClassCastException 85 | But rawtypes and casts are expected to be unsafe. 86 | 87 | Manual instantiation of `Type.App` class can be made visibly unsafe 88 | with methods like 89 | 90 | ````java 91 | protected abstract void pleaseDoNotImplementMeItIsUnsafe(); 92 | ```` 93 | 94 | (see [actual definition](https://github.com/sviperll/higher-kinded-java/blob/master/src/main/java/com/github/sviperll/higherkindedjava/Type.java#L25)). 95 | 96 | Very simple annotation processor as one implemented with [writejava4me](https://github.com/sviperll/writejava4me) 97 | is sufficient to make this all work. 98 | 99 | Try it yourself! 100 | 101 | [reddit descussion](https://www.reddit.com/r/java/comments/4wpetu/higher_kinded_types_for_java/) 102 | -------------------------------------------------------------------------------- /src/main/java/com/github/sviperll/higherkindedjava/GADT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | package com.github.sviperll.higherkindedjava; 8 | 9 | import java.util.function.Function; 10 | 11 | /** 12 | * 13 | * @author Victor Nazarov <asviraspossible@gmail.com> 14 | */ 15 | @GenerateTypeConstructor 16 | public abstract class GADT { 17 | @SuppressWarnings("rawtypes") 18 | private static final Factory FACTORY = new Factory(); 19 | 20 | @SuppressWarnings("unchecked") 21 | public static GADTVisitor> factory() { 22 | return FACTORY; 23 | } 24 | 25 | public static GADT cast(GADTTypeConstructor.Is tyConstrKnowledge, Type.Eq tyArgKnowledge, GADT value) { 26 | return tyConstrKnowledge.convertToGADT(tyArgKnowledge.toTypeApp().cast(tyConstrKnowledge.convertToTypeApp(value))); 27 | } 28 | 29 | public static GADT zero() { 30 | return GADT.factory().zero(Type.Eq.obvious()); 31 | } 32 | 33 | public static GADT succ(GADT value) { 34 | return GADT.factory().succ(value, Type.Eq.obvious()); 35 | } 36 | 37 | public static GADT isZero(GADT value) { 38 | return GADT.factory().isZero(value, Type.Eq.obvious()); 39 | } 40 | 41 | public static GADT ifClause(GADT condition, GADT thenValue, GADT elseValue) { 42 | return GADT.factory().ifClause(condition, thenValue, elseValue); 43 | } 44 | 45 | public static GADT> lam(Function> function) { 46 | return GADT.>factory().lam(function, Type.Eq.obvious()); 47 | } 48 | 49 | public static GADT app(GADT> function, GADT argument) { 50 | return GADT.factory().app(function, argument); 51 | } 52 | 53 | private GADT() { 54 | } 55 | 56 | abstract R accept(GADTVisitor visitor); 57 | 58 | public T eval() { 59 | return accept(new GADTVisitor() { 60 | @Override 61 | public T zero(Type.Eq tyArgKnowledge) { 62 | return tyArgKnowledge.cast(0); 63 | } 64 | 65 | @Override 66 | public T succ(GADT value, Type.Eq tyArgKnowledge) { 67 | return tyArgKnowledge.cast(value.eval() + 1); 68 | } 69 | 70 | @Override 71 | public T isZero(GADT value, Type.Eq tyArgKnowledge) { 72 | return tyArgKnowledge.cast(value.eval() == 0); 73 | } 74 | 75 | @Override 76 | public T ifClause(GADT condition, GADT thenValue, GADT elseValue) { 77 | return condition.eval() ? thenValue.eval() : elseValue.eval(); 78 | } 79 | 80 | @Override 81 | public T lam(Function> function, Type.Eq> tyArgKnowledge) { 82 | return tyArgKnowledge.cast((U argument) -> function.apply(argument).eval()); 83 | } 84 | 85 | @Override 86 | public T app(GADT> function, GADT argument) { 87 | return function.eval().apply(argument.eval()); 88 | } 89 | }); 90 | }; 91 | 92 | @Override 93 | public String toString() { 94 | return accept(new GADTVisitor() { 95 | 96 | @Override 97 | public String zero(Type.Eq tArgKnowledge) { 98 | return "0"; 99 | } 100 | 101 | @Override 102 | public String succ(GADT value, Type.Eq tArgKnowledge) { 103 | return value.toString() + " + 1"; 104 | } 105 | 106 | @Override 107 | public String isZero(GADT value, Type.Eq tArgKnowledge) { 108 | return "isZero(" + value.toString() + ")"; 109 | } 110 | 111 | @Override 112 | public String ifClause(GADT condition, GADT thenValue, GADT elseValue) { 113 | return "(if " + condition.toString() + " then " + thenValue.toString() + " else " + elseValue.toString() + ")"; 114 | } 115 | 116 | @Override 117 | public String lam(Function> function, Type.Eq> tArgKnowledge) { 118 | return "<#function>"; 119 | } 120 | 121 | @Override 122 | public String app(GADT> function, GADT argument) { 123 | return function.toString() + "(" + argument.toString() + ")"; 124 | } 125 | }); 126 | }; 127 | 128 | public GADT cloneAsGADT() { 129 | return cloneAsGADT(GADTTypeConstructor.GET); 130 | } 131 | 132 | private GADT cloneAsGADT(GADTTypeConstructor.Is tyConstrKnowledge) { 133 | return accept(new GADTVisitor>() { 134 | @Override 135 | public GADT zero(Type.Eq tyArgKnowledge) { 136 | return GADT.cast(tyConstrKnowledge, tyArgKnowledge, GADT.zero()); 137 | } 138 | 139 | @Override 140 | public GADT succ(GADT value, Type.Eq tyArgKnowledge) { 141 | return GADT.cast(tyConstrKnowledge, tyArgKnowledge, GADT.succ(value)); 142 | } 143 | 144 | @Override 145 | public GADT isZero(GADT value, Type.Eq tyArgKnowledge) { 146 | return GADT.cast(tyConstrKnowledge, tyArgKnowledge, GADT.isZero(value)); 147 | } 148 | 149 | @Override 150 | public GADT ifClause(GADT condition, GADT thenValue, GADT elseValue) { 151 | return GADT.ifClause(condition, thenValue, elseValue); 152 | } 153 | 154 | @Override 155 | public GADT lam(Function> function, Type.Eq> tyArgKnowledge) { 156 | return GADT.cast(tyConstrKnowledge, tyArgKnowledge, GADT.lam(function)); 157 | } 158 | 159 | @Override 160 | public GADT app(GADT> function, GADT argument) { 161 | return GADT.app(function, argument); 162 | } 163 | }); 164 | } 165 | 166 | private static class Factory implements GADTVisitor> { 167 | 168 | Factory() { 169 | } 170 | 171 | @Override 172 | public GADT zero(Type.Eq typeArgIsInt) { 173 | return new GADT() { 174 | @Override 175 | R accept(GADTVisitor visitor) { 176 | return visitor.zero(typeArgIsInt); 177 | } 178 | }; 179 | } 180 | 181 | @Override 182 | public GADT succ(GADT value, Type.Eq typeArgIsInt) { 183 | return new GADT() { 184 | @Override 185 | R accept(GADTVisitor visitor) { 186 | return visitor.succ(value, typeArgIsInt); 187 | } 188 | }; 189 | } 190 | 191 | @Override 192 | public GADT isZero(GADT value, Type.Eq typeArgIsBool) { 193 | return new GADT() { 194 | @Override 195 | R accept(GADTVisitor visitor) { 196 | return visitor.isZero(value, typeArgIsBool); 197 | } 198 | }; 199 | } 200 | 201 | @Override 202 | public GADT ifClause(GADT condition, GADT thenValue, GADT elseValue) { 203 | return new GADT() { 204 | @Override 205 | R accept(GADTVisitor visitor) { 206 | return visitor.ifClause(condition, thenValue, elseValue); 207 | } 208 | }; 209 | } 210 | 211 | @Override 212 | public GADT lam(Function> function, Type.Eq> typeArgIsFunction) { 213 | return new GADT() { 214 | @Override 215 | R accept(GADTVisitor visitor) { 216 | return visitor.lam(function, typeArgIsFunction); 217 | } 218 | }; 219 | } 220 | 221 | @Override 222 | public GADT app(GADT> function, GADT argument) { 223 | return new GADT() { 224 | @Override 225 | R accept(GADTVisitor visitor) { 226 | return visitor.app(function, argument); 227 | } 228 | }; 229 | } 230 | } 231 | } 232 | --------------------------------------------------------------------------------