A custom annotation to apply currying in Java
Tags: java
Published: 2022-06-27 04:31:44

---
title: A custom annotation to apply currying in Java
published: true
date: 2022-06-26 06:00:00 UTC
tags: java,java
canonical_url: https://jsedano.dev/java/2022/06/26/a-custom-annotation-to-apply-currying-in-java.html
---

I created a custom annotation in order to apply currying to a method or constructor.

You can find the complete code for this here: [curry](https://github.com/jsedano/curry).

For more information on currying check this [post](https://dev.to/jsedano/currying-in-java-2hm) and for a background on annotations in Java check this [one](https://dev.to/jsedano/java-annotations-khj).

First we create the annotation:

```
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface Curry {}
```

We only need the annotation to be processed and then discarded (we don’t need to have it available at run time or recorded on the .class) so we use `@Retention(RetentionPolicy.SOURCE)`.

This annotation will only be used on methods and constructors so we set those as the Target on `@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})`.

Then we create a processor for the annotation:

```
@SupportedAnnotationTypes("dev.jsedano.curry.annotation.Curry")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@AutoService(Processor.class)
public class CurryProcessor extends AbstractProcessor {
```

Using `@SupportedAnnotationTypes("dev.jsedano.curry.annotation.Curry")` we say that this processor will only look for that particular annotation.

`@SupportedSourceVersion(SourceVersion.RELEASE_17)` here we are saying the Java version supported.

The last one is pretty interesting, `@AutoService(Processor.class)` is from a Google library called [auto-service](https://github.com/google/auto/tree/master/service). In order for the Java compiler to use a processor the class needs to be declared inside the jar on the `META-INF/services`directory on the `javax.annotation.processing.Processor` file, the auto-service library does that for you.

Then we need to implement the `process` method on out custom processor.

```
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
```

Inside our class we get the `processingEnv` object that provides us with functionality such as `getMessager()` that we use to print a warning message on compile time when the @Curry annotation is used on a method with 1 or more than 10 parameters:

```
otherMethods.stream()
    .forEach(
        e ->
            processingEnv
                .getMessager()
                .printMessage(
                    Diagnostic.Kind.MANDATORY_WARNING,
                    "incorrect number of parameters, allowed only between 2 and 1O, will not generate code for this one",
                    e));
```

We also have this one `getFiler()` which allows us to create Java source files:

```
JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(builderClassName);
```

On the [tests](https://github.com/jsedano/curry/tree/main/tests) module of the project we declare some methods with the @Curry annotation, for example this constructor:

```
@Curry
public AnnotatedClass(
    boolean aBoolean, List<String> aStringList, int aNumber, char aChar, float aFloat) {
  this.aBoolean = aBoolean;
  this.aStringList = aStringList;
  this.aNumber = aNumber;
  this.aChar = aChar;
  this.aFloat = aFloat;
}
```

After running `mvn clean verify` on the parent module we can see the autogenerated code under `target/generated-sources/annotations/dev.jsedano.curry.tests`:

```
public static java.util.function.Function<java.lang.Boolean,java.util.function.Function<java.util.List<java.lang.String>,java.util.function.Function<java.lang.Integer,java.util.function.Function<java.lang.Character,java.util.function.Function<java.lang.Float,dev.jsedano.curry.tests.AnnotatedClass>>>>> pentaConstructor(dev.jsedano.curry.util.function.PentaFunction<java.lang.Boolean,java.util.List<java.lang.String>,java.lang.Integer,java.lang.Character,java.lang.Float,dev.jsedano.curry.tests.AnnotatedClass> function) {
    return v0->v1->v2->v3->v4-> function.apply(v0,v1,v2,v3,v4);
}
```

It is not pretty looking, but we can use it to then curry the five parameter constructor of the example class:

```
var pentaConstructor = AnnotatedClassCurryer.pentaConstructor(AnnotatedClass::new);
```

You can see another example [here](https://github.com/jsedano/examples/blob/main/curry-examples/src/main/java/dev/jsedano/curry/examples/WgetVersion2.java), but if you want to compile it you need to do `mvn clean install` on the curryer module of [curry](https://github.com/jsedano/curry).

```
@Curry
public static String wget(
    int connectionTimeout,
    int readTimeout,
    boolean followRedirects,
    String requestMethod,
    String address) {
  try {
    URL url = new URL(address);
    HttpURLConnection con = (HttpURLConnection) url.openConnection();
    con.setRequestMethod(requestMethod);
    con.setConnectTimeout(connectionTimeout);
    con.setReadTimeout(readTimeout);
    con.setInstanceFollowRedirects(followRedirects);
    BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
    String inputLine;
    StringBuffer content = new StringBuffer();
    while ((inputLine = in.readLine()) != null) {
      content.append(inputLine);
    }
    in.close();
    return address + " " + content.toString();
  } catch (Exception e) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    e.printStackTrace(pw);
    String stackTrace = sw.toString();
    return address + " " + stackTrace.substring(0, stackTrace.indexOf("\n"));
  }
}
```

Then we can set the values we need in a curried way and use it:

```
public static void main(String[] args) {
  var get =
      WgetVersion2Curryer.wget(WgetVersion2::wget)
          .apply(100)
          .apply(100)
          .apply(false)
          .apply("GET");

  List.of(
          "https://www.google.com",
          "https://www.wikipedia.org",
          "asdf",
          "https://docs.oracle.com/javase/10/docs/api/java/net/package-summary.html",
          "https://jsedano.dev",
          "https://raw.githubusercontent.com/center-key/clabe-validator/main/clabe.ts")
      .parallelStream()
      .map(get)
      .forEach(System.out::println);
}
```

Download the complete code from this post here: [curry](https://github.com/jsedano/curry).