curry化と部分適用をJavaで ~命名編~

この記事は アイスタイル Advent Calendar 2022 23日目の記事です。

https://qiita.com/advent-calendar/2022/istyle

# はじめに
みなさんこんにちは。
入社4年目、アイスタイルのバックエンドサービスを担当しているkuriyamayです。
アドベントカレンダー2本目の投稿になります。

1本目は`curry化と部分適用をJavaで`という内容で記事を書きました。
今回は既存メソッドをcurry化・部分適用して、さらに命名にも気をつかった記述にしたいと思います。

curry化や部分適用とは？という方は1本目の記事をご覧ください。

https://qiita.com/kuriyamay/items/45f69ceda78d88acd871



# 既存メソッドのcurry化と部分適用

2つの引数を受け取り結果を返すメソッドがあります。
例のため、分かりやすいように2つのStringを受け取り結合して返すだけの簡単な処理です。
```java
public String concat(String left, String right) {
  return left + right;
}
```

## 普通に使ってみる

入力値に対して`"prefix: "`文字列をつける場合はこのような記述になりますね。
処理する回数分`"prefix: "`をconcatメソッドに渡しているので冗長な書き方になっています。
```java
var prefixed =
    Stream
        .of("targetA", "targetB", "targetC")
        .map(target -> this.concat("prefix: ", target))
        .collect(toList()); // ["prefix: targetA", "prefix: targetB", "prefix: targetC"]
```

## curry化・部分適用
`BiFunction<String, String, String>`を引数に持ち、xを受け取り、yを受け取り結果を返すcurryメソッドを作ります。
このcurryメソッドにconcatメソッドをメソッド参照で渡してcurry化・部分適用できるようにします。

```java
// curryingのメソッド
public static Function<String, Function<String, String>> curry(BiFunction<String, String, String> biFunction) {
  return left -> right -> biFunction.apply(left, right);
}

// currying & 部分適用
var partialApply = curry(this::concat).apply("prefix: ");
var prefixed =
    Stream
        .of("targetA", "targetB", "targetC")
        .map(partialApply)
        .collect(toList()); // ["prefix: targetA", "prefix: targetB", "prefix: targetC"]
```

`this::concat`は型が`BiFunction<String, String, String>`になり関数インターフェースとして扱うことができます。
これで`"prefix: "`を部分適用した関数が作れました。


# 名前を付ける
既存メソッドをcurryingして部分適用もできましたが、部分適用するときの`apply()`という名前は何をしているのか分かりづらいです。
名前をしっかり付けることでこのメソッドを使う人や、コードを読む人のストレスを少しでも減らしてあげることができますね。

## interfaceを定義
* Curry
* LeftPartialApply


というinterfaceを定義し、先ほど作ったcurryメソッドの戻り値を`Curry`にします。
Curryには入力を受け取りLeftPartialApplyを返す抽象メソッド`left`を定義、LeftPartialApplyも入力を受け取り結果を返す`right`を定義します。
どちらも関数インターフェースの`Function<T, R>`を継承させます。
下記のような形ですね。

```java
public static Curry curry(BiFunction<String, String, String> biFunction) {
  return left -> right -> biFunction.apply(left, right);
}

public interface Curry extends Function<String, LeftPartialApply> {
  LeftPartialApply left(String s);

  @Override
  default LeftPartialApply apply(String s) {
    return this.left(s);
  }
}

public interface LeftPartialApply extends Function<String, String> {
  String right(String s);

  @Override
  default String apply(String s) {
    return this.right(String);
  }
}
```

このような使い方になります。
```java
var leftPartialApplyForConcat = curry(this::concat).left("prefix: ");
var prefixed =
    Stream
        .of("targetA", "targetB", "targetC")
        .map(leftPartialApplyForConcat::right)
        .collect(toList()); // ["prefix: targetA", "prefix: targetB", "prefix: targetC"]
```

concatの左側に部分適用させるのでcurryに続くメソッド名は`left`としました。
部分適用した関数を使うときはconcatの右側に値が渡るのでこちらは`right`としています。

`left/right`で表現することでコードを読んだときに直感的に分かるようになりました。


# 一般化する

上記は型がStringなので一般化します。

```java
public static <T, U, R> Curry<T, U, R> curry(BiFunction<T, U, R> biFunction) {
  return left -> right -> biFunction.apply(left, right);
}

public interface Curry<T, U, R> extends Function<T, LeftPartialApply<U, R>> {
  LeftPartialApply<U, R> left(T t);

  @Override
  default LeftPartialApply<U, R> apply(T t) {
    return this.left(t);
  }
}

public interface LeftPartialApply<U, R> extends Function<U, R> {
  R right(U u);

  @Override
  default R apply(U u) {
    return this.right(u);
  }
}
```

使用するときの記述は変わらずです。
```java
var leftPartialApplyForConcat = curry(this::concat).left("prefix: ");
var prefixed =
    Stream
        .of("targetA", "targetB", "targetC")
        .map(leftPartialApplyForConcat::right)
        .collect(toList()); // ["prefix: targetA", "prefix: targetB", "prefix: targetC"]
```



# おまけ

左側ではなく右側を部分適用したいときもきっとありますよね。
そんなユースケースに備えてどちらからでも部分適用できるようにします。

```java
// Right
public static <T, U, R> RightCurry<T, U, R> rightCurry(BiFunction<T, U, R> biFunction) {
  return left -> right -> biFunction.apply(right, left);
}

public interface RightCurry<T, U, R> extends Function<U, RightPartialApply<T, R>> {
  RightPartialApply<T, R> from(U t);

  @Override
  default RightPartialApply<T, R> apply(U u) {
    return this.from(u);
  }
}

public interface RightPartialApply<T, R> extends Function<T, R> {
  R left(T t);

  @Override
  default R apply(T t) {
    return this.left(t);
  }
}

// Left
public static <T, U, R> LeftCurry<T, U, R> leftCurry(BiFunction<T, U, R> biFunction) {
  return left -> right -> biFunction.apply(left, right);
}

public interface LeftCurry<T, U, R> extends Function<T, LeftPartialApply<U, R>> {
  LeftPartialApply<U, R> from(T t);

  @Override
  default LeftPartialApply<U, R> apply(T t) {
    return this.from(t);
  }
}

public interface LeftPartialApply<U, R> extends Function<U, R> {
  R right(U u);

  @Override
  default R apply(U u) {
    return this.right(u);
  }
}
```

これで右側左側、どちらからでも部分適用できるようになりました。
```java
var leftPartialApplyForConcat = leftCurry(this::concat).from("prefix: ");
var prefixed =
    Stream
        .of("targetA", "targetB", "targetC")
        .map(leftPartialApplyForConcat::right)
        .collect(toList()); // ["prefix: targetA", "prefix: targetB", "prefix: targetC"]

var rightPartialApplyForConcat = rightCurry(this::concat).from(": suffix");
var suffixed =
    Stream
        .of("targetA", "targetB", "targetC")
        .map(rightPartialApplyForConcat::left)
        .collect(toList()); // ["targetA: suffix", "targetB: suffix", "targetC: suffix"]
```



# 最後に

既存メソッドをcurry化して部分適用できるようにし、さらに使いやすさを考慮した名前付けもやってみました。
curry化・部分適用についてもっと知りたいと思った方は是非お手元の環境でやってみてください。
関数への理解も進みエンジニアとしての幅も広がるはずです。

アドベントカレンダーも残すところあと2日となりました。
アイスタイルではいろんなジャンルの記事を書いているのでよかったらご覧になってください。

https://qiita.com/advent-calendar/2022/istyle