7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

株式会社ACCESSAdvent Calendar 2018

Day 3

Go 言語の標準ライブラリから多値の戻り値についての思想をでっち上げる

Last updated at Posted at 2018-12-02

株式会社 ACCESS の Advent calendar へようこそ。僕は ACCESS 従業員の三原と申します。この記事の題名にいきなり Go 言語と書いていますが、僕は今まで C 言語を生業としていて、Go 言語を使ったのはここ半年ほどです。そんな初心者の僕が、Go 言語の使い方はこんなものなのかな、と思ったところを述べさせていただきます。

Go 言語の文法だと、多値の戻り値は素直でない?

Go 言語では関数が多値の戻り値を返せることが特徴と言われています。

しかしプログラミング言語に造詣が深い人からは、文法・機能が素直でないと指摘されます。関数の戻り値を変数に入れることなく同じ型シグネチャの引数として渡せないといった制約があるためです。

……なのですが、僕は Go 言語の標準ライブラリを使っている限りにおいて、制約だとは思っていませんでした。何も考えていなかっただけなのですが、ある程度は自然だと思えた理由を述べるのが、この記事の目的です。

言語機能と標準ライブラリからいくつか

ここで、Go 言語の言語機能と標準ライブラリに触れさせていただきます。

多値のようで多値でないから素直でない?

Go 言語の言語機能と標準ライブラリにて、多値のようで多値でないと指摘される点があります。

1つは Type Assertion

var i foo
j, ok := i.(bar)

このとき ji を型変換したものですが ok は型変換の成否を表しており、2つは意味が違います。このように意味が違う変数が混ざる点が多値のようで多値でないと指摘されます。

もう1つは Error を返すもの。ここでは File.Read() を例にとります。
https://golang.org/pkg/os/#File.Read

// 変数 file にファイルを open しているものとします
len, err := file.Read(buf)

このとき len は読み込んだデータの長さですが err は Error の有無を表しており、2つは意味が違います。これも意味が違う変数が混ざるので多値のようで多値でないと指摘されます。

でも、Go 言語でソフトウェアを実装しているとき、多値の戻り値を扱うのはこういうケースがむしろ主だったりしませんか?

多値のようで多値でないのが本義?

この、多値のようで多値でないケースをむしろ Go 言語で関数が多値の戻り値を返す本義だと思ってしまえば、それは不思議ではなくなります。

上に挙げた例では、一方の変数の値によって他方の変数の解釈が変わります。一方が他方の解釈を変える、それが Go 言語での多値の戻り値の意義と目的なのではないか。

ある情報の解釈を変えてしまう情報、それは「メタ」です。上の例では、一方がベタな情報で、他方がその解釈を変えるメタ情報です。

ベタな情報とメタ情報を同時に返すこと。それが Go 言語の言語機能と標準ライブラリに繰り返し現われるパターンです。

多値の戻り値を返すのはレベルが違う情報を同時に返すとき、というのが Go 言語での多値の戻り値の本義なのではないか。常に同時に同じレベルで扱う情報を複数返すときには struct を定義することにしていないか。そんなことを思ってみるわけです。言語設計者に確認していない妄想ですが。

そう思うと、多値の戻り値をそのまま関数の引数にできないことは、不便ではありますが、故が無いわけではないです。常に全ての戻り値を参照することが一般的ではないこと、例外条件が2つの関数で一致するとは限らないこと、を考えると、多値の戻り値をそのまま関数の引数に渡すことは見えないバグの温床になりかねません。その点から見ると、いったん変数に入れるというクッションを置くことも、ありといえばあり、です。

また、このことは、Go 言語の多値の戻り値が他言語の例外のように使えることの素直な解釈に結びつきます。他言語ではレベルの違う情報を戻り値と例外に分けているところを、Go 言語では多値の戻り値のそれぞれがレベルが違う、という素直な対応に結びつきます。

C 言語からなにが変わったか

ここで C 言語での標準ライブラリの例として POSIX の read() 関数を引きます。(下記 URL は Linux の man ページ)
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/read.2.html

read() 関数の戻り値は

  • 1 以上 ... 読み込んだデータの長さ
  • 0 ... ファイルディスクリプ他の終わりに到達した
  • -1 ... エラー。詳細は変数 errno を参照

戻り値が1つの C 言語では、値域によって戻り値の意味を変えていました。

Go 言語では意味を変える役目を別の戻り値に別けた、ということなのかもしれません。

それさえ達成できればよいと思っていたのか、その答えを聞いたわけではありませんが。

明日は @komitake さんです。どうぞお楽しみに。

7
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?