JavaのOptionalをgolangにportする - Method Reference と Method values

[JavaのOptionalをgolangにportする](https://qiita.com/shinnara/items/dca2181717e72b92d10d) が他の記事に比べて見られてます。といっても3桁程度ですが。タイトル詐偽的な状況になってしまったのかなと反省してます。今回も大した事は書いてないのでゆるゆるお付き合いください。

# 前回までのあらまし
* Javaを勉強するために、golangでJavaの[Optional](https://github.com/openjdk/jdk/blob/jdk-21%2B35/src/java.base/share/classes/java/util/Optional.java#L86)を実装してみます
* Optionalをいきなり実装できないので、まず[Function](https://github.com/openjdk/jdk/blob/jdk-21%2B35/src/java.base/share/classes/java/util/function/Function.java)を実装します
* golangで[Applier](https://github.com/dairyo/j2g/blob/42be6074090b96e28c08da65e4af5e7040b830da/java/util/function/function/function.go)を作ってみました

# 今回のあらすじ
今回は、「Applierを作ったけど、[Method values](https://go.dev/ref/spec#Method_values)があるからインターフェースいらなかった。Method valuesを使えばJavaのFunctional interfaceみたいなことができる。」という話をします。

# なぜ Applier interface を定義したのか
Applier interface を定義した理由は、golang では method を変数に代入できないと思っていたからです。このため、methodをApplierに適合させるためには以下の様な方法を考えていました。

```go
package main

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/dairyo/j2g/java/util/function"
)

type builder struct {
	strings.Builder
}

func (b *builder) Apply(s string) (int, error) {
	return b.WriteString(s)
}

func main() {
	sb1 := &strings.Builder{}
	f1 := function.Compose(
		function.NewApplierFuncWithNoError(strconv.Itoa),
		// methodを関数リテラルで囲まないといけないとおもっていた。
		function.NewApplierFunc(func(s string) (int, error) { return sb1.WriteString(s) }),
	)
	f1.Apply(10)
	fmt.Println(sb1.String())

	sb2 := &builder{}
	f2 := function.Compose(
		function.NewApplierFuncWithNoError(strconv.Itoa),
		// または集約を使って、Applierに適合させる。
		sb2,
	)
	f2.Apply(10)
	fmt.Println(sb2.String())
}
```

しかし、上のような冗長なことをしなくても、 Method values を使えば関数変数に直接 method を代入できます。

# Java の Method References
## Lambda

golang の Method values の話をする前に対応する Java の Lambda と Method Reference の説明をします。

Java の Lambda は [JDK 8](https://www.oracle.com/java/technologies/javase/8-whats-new.html) で導入された機能です。リリースノートでは以下の様に説明されています。

> Lambda Expressions, a new language feature, has been introduced in this release. They enable you to treat functionality as a method argument, or code as data. Lambda expressions let you express instances of single-method interfaces (referred to as functional interfaces) more compactly.

要約すると

1. 機能性(functionality)をメソッドの引数として扱える
2. functional interfaceという1つのメソッドのみを持つ interface の instance を簡単に表現できる

Lambda の説明のためにまず [functional interface](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/FunctionalInterface.html) について説明します。functional interfaceとは抽象メソッドが1つだけあるinterfaceです。制約は抽象メソッドが1つだというものなので static method や defalut method が複数存在しても問題ありません。

Javaの[Fuction](https://github.com/openjdk/jdk/blob/jdk-21%2B35/src/java.base/share/classes/java/util/function/Function.java)は抽象メソッドが `apply` しかないので functional interface です。このため Lambda を代入できます。

```java
import java.util.function.Function;

public class Main  {
    public static void main(String[] args) {
        // Function に Lambda を代入
        Function<String, Integer> f = s -> s.hashCode();
        System.out.println(f.apply("foo"));
    }
}
```

しかし、上の書き方だと `String.hasCode` を使いたいだけなのに Lambda を書かなければならず少し面倒です。そこで使われるのが Method Reference です。 

## Method Reference
Method Reference はメソッドへの呼出しを Lambda と読み替えてくれる機能です。以下に例を記載します。

```java
import java.util.function.Function;

public class Main  {
    public static void main(String[] args) {
        // Method Reference を利用
        Function<String, Integer> f = String::hashCode;
        System.out.println(f.apply("foo"));
    }
}
```

Method Reference は以下の4種があります。

1. static method に対する参照
2. 特定のオブジェクトの instance method に対する参照
3. 任意のオブジェクトに対する特定の型の instance method に対する参照
4. コンストラクタに対する参照

それぞれの詳細は公式ドキュメントの [Method References](https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html)をご覧下さい。今回利用したのは 3 の `任意のオブジェクトに対する特定の型の instance method に対する参照` になります。

ここまでで Lambda のリリースノートの要約で書いた `functional interfaceという1つのメソッドのみを持つ interface の instance を簡単に表現できる` は分かったかと思います。最後に上のものを Lambda や Method Reference を使わないで書きます。 Lambda や Method Reference と言っても関数自体を作っているわけではありません。 Java はいくつかの基本型を除けばクラス型しかないので、 Lambda や Method Reference で作られるものも、結局無名クラスでしかないのです。

```java
import java.util.function.Function;

public class Main  {
    public static void main(String[] args) {
        // Method Reference を利用
        var f = new Function<String, Integer>() {
            public Integer apply(String s) {
                return s.hashCode();
            }
        };
        System.out.println(f.apply("foo"));
    }
}
```

上の例を見るとわかるように、 Lambda 相当のコードは Java7 以前も書けたのです。ただし、 Lambda とそれに付随する機能が追加された事でより簡単に書けるようになったことがわかると思います。その点を明確にするため、要約のもう一つ `機能性(functionality)をメソッドの引数として扱える` を説明したいと思います。 `なぜ Applier interface を定義したのか` の最初に書いた golang のサンプルを Java を使ってかいてみます。

```java
import java.util.function.Function;

public class Main  {
    public static void main(String[] args) {
        // Method Reference を利用
        Function<Integer, String> f = i -> i.toString();
        var sb = new StringBuilder();
        sb = f.andThen(sb::append).apply(10);
        System.out.println(sb.toString());
    }
}
```

StringBuilder の append メソッドを Method Reference を使って最初の `f` のオブジェクトに直接渡せます。これが `機能性(functionality)をメソッドの引数として扱える` という意味だとおもっています。ちなみに `andThen` の引数で使われている Method Reference は `特定のオブジェクトの instance method に対する参照` になります。

# golang の Method Values
## golangの関数

上で書いたように Java には関数自体はなく、無名クラスを簡単に作成することで擬似的に関数を表現しています。これに対して golang は関数が存在します。そして[第一級関数](https://ja.wikipedia.org/wiki/第一級関数)です。このため golang の関数は変数に代入できます。例えば以下の様なコードを書けます。

```go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	f1 := func(i, j int) int { return i + j }
	f2 := strconv.Itoa

	fmt.Println(f1(1, 1))
	fmt.Println(f2(10))
}
```

これは golang の利用者ならば大体の方がしっていることだと思います。この関数の変数への代入ですが、メソッドでも可能です。それを [Method Values](https://go.dev/ref/spec#Method_values) といいます。

## Method Values
Method Values を使うと以下の様なコードを書けます。

```go
package main

import (
	"fmt"
	"strings"
)

func main() {
	b := &strings.Builder{}
	var f func(string) (int, error) = b.WriteString
	f("foo")
	fmt.Println(b.String())
}
```

関数変数に入れる事を明示するために `var f func(string) (int, error) = b.WriteString` と書いていますが、もちろん `f := b.WriteString` でも大丈夫です。これは Java の Method Reference の `2. 特定のオブジェクトの instance method に対する参照` に対応すると思います。 

## Method Expressions
golang には [Method Expressions](https://go.dev/ref/spec#Method_expressions) というものもあり、こちらは`3. 任意のオブジェクトに対する特定の型の instance method に対する参照` に対応すると思います。以下に例を記載します。

```go
package main

import (
	"fmt"
	"strings"
)

func main() {
	var f func(*strings.Builder, string) (int, error) = (*strings.Builder).WriteString
	b := &strings.Builder{}
	f(b, "foo")
	fmt.Println(b.String())
}
```

`1. static method に対する参照` と `4. コンストラクタに対する参照` は関数を関数変数に代入することで対応すると思うので、 Java の Method Reference 相当の処理は全て golang で出来ることになります。

# Applier interface の修正
こうなると「そもそも Applier interface 必要?」ということになってきます。全て関数型の変数に代入してしまえばいいのではないかと。

というわけで、そうしました。 修正後のコードは以下のものになります。

* [function.go](https://github.com/dairyo/j2g/blob/42be6074090b96e28c08da65e4af5e7040b830da/java/util/function/function/function.go)

前回からの相違点は以下の通りです。

1. `Applier` interface を削除し、 `Function` 型を導入した
   * `Function` 型の定義は `func(T) (U, error)` です
2. Compose関数の引数と戻り値は両方とも `Applier` から `Function` に変更した
3. functionディレクトリの下にさらにfunctionディレクトリを作成しました
   * 今後、Javaの `Predicate` や `Consumer` を実装する際に、同じ名前の関数を作りたいことがおこりそうなので、 package をわけた

修正したバージョンで最初の例を書き直すと以下の様になります。

```go
package main

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/dairyo/j2g/java/util/function/function"
)

func main() {
	sb := &strings.Builder{}
	f1 := function.Compose(
		function.WrapNoErrFunc(strconv.Itoa),
		sb.WriteString,
	)
	f1(10)
	fmt.Println(sb.String())
}
```

多少簡潔になりました。また引数の渡し方が一意に決まりました。

# まとめ
二回目でも `Function` のportが終らないというゆるゆるした進みになっています。しかし、 Java の Lambda の勉強が出来て、 golang も知らなかった機能を知れたので有意義な時間になりました。実装方針も少しずつ固まってきているので、 今後は `Predicate` は `Consumer` を実装していきたいと思います。

# 参考文献

* Java
  * [Lambda Expressions](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)
  * [Method References](https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html)
* golang
  * [Method expressions](https://go.dev/ref/spec#Method_expressions)
  * [Method values](https://go.dev/ref/spec#Method_values)