質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.96%

モダンな言語で型が後置な理由

解決済

回答 10

投稿 編集

  • 評価
  • クリップ 10
  • VIEW 10K+

score 109

タイトル通りのシンプルな質問なのですが、調べてみたものの、
しっくりくる回答がなかったため、質問いたします。

C/C++やJava, C#など静的型付け言語のうち比較的レガシーなものは、
変数定義時の型は前置が主流だと思います。

int x = 3;
int add(int x, int y) {
  return x + y;
}

しかしながら、TypeScript, Rust, Goなどのモダンな言語では後置を採用していることが多いです。
これだけ揃っていると偶然とは思い難く、何かしらのメリットがあるのだろうと想像します。

一体どんなメリットがあるのか、ご存知でしたらご教示頂けたら幸いです。

function add(left: number, right: number): number {
    return left + right;
}
type Circle struct {
    radius float64
}
fn factorial(i: u64) -> u64 {
    (1..=i).product()
}


よろしくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 10

checkベストアンサー

+16

StackOverflowでも同じような質問をされている方がいます。

回答を大雑把にまとめると、

(1) 型推論のある言語では、型宣言をしなくても問題ない場合がある

var myNumber = 1
// 機械はmyNumberをnumberとして評価できる

(2) 必ずしも必要でない記述を右側にもっていくことで、ソースコードを解釈するときの曖昧性を軽減し、パフォーマンスを向上できる

number var myNumber = 1

上の記述はイメージですが、機械の立場で言うと、numberの後のvarまで到達しないと、変数宣言かどうかわかりません。変数宣言に必ず必要な「var」と「変数名」が左にくれば、パースのパフォーマンスがあがります。また、(回答者によれば、議論のよちはありますが)人間にとっても可読性があがります。

C++などではオプショナルなものも左に書きますが、int* a, b;ではaはポインターですが、bはそうではありません。現在では、C++にもauto x = int{ 4 };のような後置の型の構文があるようで、いくつかの利点があるようです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/12 10:19

    ご回答ありがとうございます。申し訳ありません、英語の情報までは調べ切れておりませんでした。
    良質なリンクのご紹介ありがとうございます。
    確かに型注釈がオプショナルであれば、後ろに持っていくのは道理ですね。
    合ったり無かったりするものが先頭にあれば当然人間(の指)にとっても混乱します。

    ありがとうございました。

    キャンセル

  • 2019/05/14 15:24

    このような 素朴な疑問(トピ主様のご質問etc.)こそ、
    意義がある、と思いました。

    小生も、見習いたく存じます。ホント素晴らしい質問です。なぜなら、老若男女スキルを問わず、閲覧されている可能性が高いと推察するから。

    質問者さまに感謝です。
    (もちろんテラテイル主催者様、回答者さま方にも、感謝します)

    キャンセル

+9

これらの言語では型が省略可能だからではないでしょうか。

私はTypeScriptやGoはよく知らないので間違っているかもしれませんが、TypeScriptでは関数の引数の型注釈を省略して以下のように書けたと思います。

function add(left, right) { .. }

Rustは関数の引数と戻り値については型を省略できない仕様になっていますが、クロージャなら省略可能です。

// クロージャで型を省略した場合
let factorial = |i| (1..=i).product();

// クロージャで型を明示した場合
let factorial = |i: u64| -> u64 { (1..=i).product() };

また変数を導入するlet文でも上のfactorial変数のように通常は型注釈は省略されます。しかし明示もできます。

// factorial変数の型を明示
let factorial: fn(u64) -> u64 = |i| (1..=i).product();

型注釈が省略できる言語では、後置にすれば、省略してもしなくても引数名や変数名の位置は同じになります。文法に統一感が出るので人間にとって読みやすくなりますし、言語処理系にとっても構文解析がしやすくなるというメリットがあると思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/12 10:24

    おっしゃる通りの内容ですっきり理解できました。
    Rustの例文もありがとうございます。
    (余談ですがRustに興味を持っているのでフォローさせて頂きました)
    ご回答ありがとうございました。

    キャンセル

+5

まず前提から整理すると、C/C++で変数の型を記述する方法は前置ではありません。

#include <stdio.h>

int add(int x, int y) { return x + y; }
int mul(int x, int y) { return x * y; }

int main(int argc, char **argv) {
    int (*functions[2])(int, int) = {add ,mul};

    printf("functions[0](2, 3) = %d\n", functions[0](2, 3));
    printf("functions[1](2, 3) = %d\n", functions[1](2, 3));
    return 0;
}

上記のコードで functions は「int型を2つ取ってint型を返す関数へのポインタの配列」です。
このように、元のCの変数宣言の仕様は複雑な型の場合には破綻します。
(これが訓練せずとも一般に読み書きしやすいとは言えないでしょう)

JavaやC#などでは前置を採用し、更にこのような複雑な型をそもそも書かせないようになっていました。
型が複雑になる前にインターフェースにしてしまうことで回避しますが、言語組み込みではないので、どのようにインターフェース化するかを統一しないと汎用性が下がってしまいます。
(近年では公式に java.util.function.Function などができて統一されました)

きちんと関数の配列のようなものを言語仕様レベルでサポートしたい場合、完全に前置にするか後置にすることでこの問題を回避できます。

package main

import "fmt"

func add(x, y int) int { return x + y }
func mul(x, y int) int { return x * y }

func main() {
    var functions [2]func(int, int) int
    functions[0] = add
    functions[1] = mul

    fmt.Printf("functions[0](2, 3) = %d\n", functions[0](2, 3))
    fmt.Printf("functions[1](2, 3) = %d\n", functions[1](2, 3))
}

完全に前置にする場合、長い型が出てくると変数名がかなり後ろになってしまいますし、パース時も型がどの部分なのか判定しづらいので、後置にするという選択がされたのだと考えています。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/14 15:10

    こんにちは。
    自分には、こちらの回答が理路明晰ゆえ、気に入りました。
    ありがとうございました。参考になりました。

    ★誤植(誤記)?:もし、小生の勘違いなら、お許し戴きたいですが、
     最終段落↓の★部分は、正しくは「後置」なのかしら?

    『完全に前置にする場合、長い型が出てくると変数名がかなり後ろになってしまいますし、パース時も型がどの部分なのか判定しづらいので、★前置にするという選択がされたのだと考えています。』

    キャンセル

  • 2019/05/14 17:20

    ご修正、ありがとうございました。

    キャンセル

  • 2019/05/19 16:10

    関数ポインタを勉強しなおしてみましたが、確かにおっしゃるとおりですね。
    すっきりした考え方が得られました。ありがとうございました。

    キャンセル

+3

もう言語の比較対象に挙げられることすらなくなってしまったけれど
VBは
Dim A as Integer
と、後でしたね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

(以下は全部根拠のない私見です)

Cコンパイラは、今では珍しい事に1パスのコンパイラであり
前から後ろに読み取っていき、基本的に戻りません。
多分、初期のコンピュータの記憶デバイスがテープだった事やメモリが貧弱だった事と関係があったと思っています。

そのため、(配列と関数ポインタを除き)型を前置することが自然でした。

言ってしまえば前置だったのはコンピュータ側の都合です。


型推論を行うようになった理由を考えると
C++においては

Hoge* f = new Hoge();
std::unorderd_map<std::string, std::vector<std::vector<unsigned long long>>> vec = f->list;

のように

  • 1つの変数に対し同じ型名を2度書く場面がたまにあること
  • とてもとても長い型名がSTLを使うと日常的になったこと

が理由だったと推測します。


そして、型推論を得た結果

auto f = new Hoge();
auto vec = f->list;

今度は人間が型が分からなくなる事態が発生しました。

それはそれで困ります。
しかし、長い型を読むのは辛いですしどこが変数名なのかが分からなくては困ります。

そういった事を考えた結果、最近の言語では後置にすることで変数名をわかりやすくしているのかと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/15 16:16

    1パスコンパイラの都合に合っているのはむしろ後置かと存じます。
    Pascalのコンパイルが速かったのもそのためと。
    (Cの場合、型名を読み取ってもその時点では変数宣言が始まったのか関数宣言が始まったのか確定しておらず、確定するところまで読み進んでバックトラックしなくてはいけません。型後置のPascalなら未定義の識別子を読んだ時点で変数宣言と確定します)

    キャンセル

+2

関数でも変数でも、
評価後の結果としてどのような値が取れるか?という事を考えてみると直感的に後置なると思いました。
関数型言語での表記(ramdajsのドキュメントにも見られる)とも一致しています。
https://ramdajs.com/docs/#append

a → [a] → [a]

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

前置は型宣言
後置は型注釈で概念が異なります

質問者様が挙げられている関数のシグネチャの例では前置だろうが後置だろうがどっちでもよくで
文法の違いに過ぎないと思ってしまいますが

型注釈の場合は、(変数宣言だけではなく)任意のに対して注釈をつけられます

1: Long
5 * 6: Long

型注釈というのは型推論が前提になっていて
型宣言がなくてもコードを書ける、だが推論に必要な付加情報を任意の場所に埋め込めるようにしよう
という設計思想だと思います

その場合後置のほうが文法的に都合がよかったのではないでしょうか
変数に対する注釈だろうがリテラルに対する注釈だろうが書き方が一緒で一貫性があるのもいいですね

型宣言型の言語だと一応キャストの文法があるけど括弧の数が増えちゃうから
型推論型の言語では採用されなかったのではないでしょうか

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/19 16:04

    ご回答有り難うございます。実際に型注釈を要する言語を触ってみてイメージが分かりました。
    注釈を入れるとエラーメッセージがわかりやすくなったりしますね。
    いろいろ勉強になりました。

    キャンセル

+1

理由は統一のためです。

「前置」だという言語でも、関数引数のカッコ(=関数型であることを意味する修飾)は、関数名の識別子に対して後置にするものが多いですが、前置と後置がごっちゃになっています。
変数の型修飾も、関数に対するものと同じ後置にすることで初めて統一がとれ、統一性を重視する言語設計者を満足させます。

もちろん、関数としての型修飾も含めて、すべて前置に統一することも可能ではあるでしょうし、実際にそんな言語も存在するのかもしれません。
こんな感じでしょうか。

int (int arg)func {...
}

でも、恐らく人気はでないのではないでしょうか。

なお、後置の型修飾は関数以外に配列の[]などでも同じ議論ができます。Cや初期のJavaでは前置後置混在になりました。Javaではある時点で前置になりましたが、違和を感じるひともいたのではないでしょうか。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/19 16:07

    確かに・・・。関数のカッコなどあまり意識したことなかったです。
    そう考えると確かにモダンな文法のほうが洗練されているような気はたしかにします。
    ご回答有り難うございました。

    キャンセル

0

重複送信したので削除

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-1

UML図では

filed_name: number
method(): number

のようの型を後ろに書きます。これに倣っているのではないでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.96%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る