名前付き引数
Last Updated: 2026-09-28 02:00
結論
melangでは、関数呼び出し時に名前付き引数を必須とする。すべての引数に、定義側の引数名をそのまま書かせる。
fn resize(width: Int, height: Int) -> Image { ... }
resize(width: 10, height: 20) // OK
resize(10, 20) // NG: 位置引数だけの呼び出しは禁止
- 呼び出し専用の別ラベル(Swiftの外部引数名のような仕組み)は持たない。定義側の引数名が、そのまま呼び出し時に書く名前になる。
- 名前の記法は、変数宣言と同じ
name: valueのpostfix形式にする。「型注釈の位置」のnoteで採用したpostfix方式(name: Type)と揃え、名前を先に読ませる。 - 引数の順序は、定義順のまま変更できないこととする(順序の入れ替えはこのnoteでは扱わない。後述)。
名前付き引数を必須にする理由
Kotlin・Python・C#などの多くの言語は、名前付き引数を「書いてもよい」機能として提供し、位置引数だけの呼び出しも許す。この場合、名前を書くかどうかは呼び出す側の裁量に委ねられ、同じ関数でも呼び出し箇所によって読みやすさが変わる。
melangは、コードを読んだときに処理の意味を追いやすいことを言語設計の前提としており(「I/O の明示」のnoteと同じ方針)、引数の意味も呼び出し箇所ごとの裁量に委ねず、常に呼び出し側のコードから読み取れるようにしたい。特に、同じ型の引数が複数並ぶ場合(resize(width, height)のような呼び出し)は、位置だけでは取り違えに気づけない。
resize(20, 10) // widthとheightが逆でも、見た目だけでは気づけない
resize(width: 20, height: 10) // 名前を書けば、取り違えが呼び出し側から見える
名前付き引数を必須にすると、関数のシグネチャが変わったとき(引数の追加・順序変更)にも、呼び出し側の意図(どの値をどの引数に渡しているか)が名前によって保たれる。位置引数だけの呼び出しでは、引数を追加したり順序を変えたりすると、既存の呼び出し箇所が意図せず別の引数に値を渡してしまう危険がある。
検討した案
Swiftのように、定義側の引数名とは別に呼び出し用のラベル(外部引数名)を持たせる設計も検討したが、採用しなかった。ラベルと引数名が分かれると、定義を読むときと呼び出しを読むときで異なる名前を覚える必要があり、「名前で意味を明示する」という目的に対して複雑さが増える。melangでは、定義側の引数名をそのまま呼び出し側の名前として使う、Kotlinに近い一枚看板の設計にする。
また、Kotlin・C#のように「位置引数と名前付き引数を併用できる」設計も検討したが、採用しなかった。併用を許すと、同じ関数でも呼び出し箇所によって名前を書いたり書かなかったりでき、必須にする意味が薄れる。
このnoteで決めないこと
- 名前付き引数にしたことで、呼び出し時に引数の順序を自由に入れ替えられるようにするか(Kotlinのように順序自由にするか、Swiftのように定義順を保つか)。
- デフォルト値を持つ引数(オプション引数)を設けるか。
- 可変長引数(varargs)との組み合わせ方。
- コンストラクタ呼び出し(値の生成)でも同様に名前付き引数を必須とするか。
他言語比較
名前付き引数の設計は、大きく次の4つに分かれる。
- 言語機能として持たない: 引数はすべて位置で渡し、意味を明示したい場合は構造体やオブジェクトで代替する(Rust・C++・Go・Java・Haskell・Zig)。
- 任意で使える: 名前付き引数と位置引数を併用でき、書くかどうかは呼び出し側が選べる(Kotlin・C#・Scala・Python・PHP・Ruby)。
- 既定で必須に近い: 引数ラベルが呼び出し側に基本的に現れる(Swift)。ラベル付き引数(OCaml・MoonBit)も、ラベルを省略する構文は用意されていない。
- オブジェクト・キーワードリストで代替する: 関数の仕組みとしては持たないが、オブジェクトやキーワードリストを渡すことで似た書き心地を実現する(TypeScript・JavaScript・Elixir)。
| 言語 | 名前付き引数 | 呼び出し時の書き方 | 位置引数との併用 | 説明 |
|---|---|---|---|---|
| Rust | なし | 位置引数のみ | -(構造体で代替) | 名前付き引数はなく、意味を明示したい場合は構造体リテラルを渡す。 言語機能としての名前付き引数はなく、これまでも提案はあるが採用されていない。意味を明示したい場合は、フィールド名を持つ構造体を渡すことが一般的な代替手段になる。 |
| MoonBit | あり(labelled arguments) | foo(~width=10, ~height=20) | 併用可(ラベルなし引数と混在可) | OCamlに近いラベル付き引数を持ち、省略可能なデフォルト値付き引数もある。 定義側で引数名の前に`~`を付けるとラベル付き引数になり、呼び出し側も`~label=value`の形で渡す。OCamlのラベル付き引数に近い設計で、変数名がラベルと同じ場合は`~label`と省略できる。 |
| Swift | あり(既定で必須) | foo(width: 10, height: 20) | 基本併用しない(ラベルが既定) | 引数ラベルが既定で呼び出し側に必須になり、省略するには`_`を明示する。 他言語のように「名前付き引数を選べる」のではなく、引数ラベルを書くことが既定の呼び出し方になっている。省略したい場合は定義側で`_`を明示する必要があり、位置引数のみの呼び出しのほうが例外的な扱いになる。 |
| Kotlin | あり | foo(width = 10, height = 20) | 併用可 | 名前付き引数と位置引数を併用でき、デフォルト引数と組み合わせてよく使われる。 名前付き引数と位置引数を併用でき、名前付きにすれば順序も自由に変えられる。デフォルト引数と組み合わせることで、オプションが多い関数を簡潔に呼び出せる。 |
| Java | なし | 位置引数のみ | - | 名前付き引数はなく、オプションが多い場合はBuilderパターンで代替する。 言語機能としての名前付き引数はない。引数が増えた場合はBuilderパターンでメソッド名に意味を持たせることが多い。 |
| Scala | あり | foo(width = 10, height = 20) | 併用可 | 名前付き引数と位置引数を併用でき、デフォルト引数と組み合わせて使われる。 Kotlinと同様、名前付き引数と位置引数を併用でき、デフォルト引数と組み合わせて使うことが多い。 |
| C# | あり | Foo(width: 10, height: 20) | 併用可(名前付きは位置引数の後ろ) | 名前付き引数と位置引数を併用でき、オプション引数と組み合わせてよく使われる。 C# 4.0で導入。位置引数の後ろに名前付き引数を続けられるが、名前付き引数の後に位置引数を書くことはできない。オプション引数と組み合わせ、多数の引数を持つAPIを呼びやすくする目的で使われる。 |
| C++ | なし | 位置引数のみ | - | 名前付き引数はなく、C++20の指定初期化子は集成体の初期化にのみ使える。 関数呼び出しに名前付き引数の構文はない。C++20で追加された指定初期化子(designated initializers)は集成体の初期化にのみ使え、関数呼び出しの引数を名前で指定することはできない。 |
| C | なし | 位置引数のみ | -(構造体で代替) | 名前付き引数はなく、C99の指定初期化子は構造体の初期化にのみ使える。 関数呼び出しに名前付き引数の構文はない。C99で追加された指定初期化子(designated initializers)は構造体・配列の初期化にのみ使え、C++20の集成体初期化子はこの機能をそのまま受け継いでいる。可変長引数(`...`と`stdarg.h`)を使えば引数の個数を柔軟にできるが、型安全性は呼び出し側の規律に委ねられ、`printf`系関数のフォーマット文字列と実引数の不一致は典型的な未定義動作の原因になる。 |
| Go | なし | 位置引数のみ | - | 名前付き引数はなく、オプション値は構造体やFunctional Optionsで表現する。 名前付き引数はなく、オプションが多い関数では構造体を渡すか、Functional Optionsパターンで意味を持たせるのが一般的。 |
| Zig | なし(struct引数で代替) | foo(.{ .width = 10, .height = 20 }) | -(構造体のフィールドデフォルト値と併用) | 名前付き引数はなく、デフォルト値を持つstructをanonymous struct literalで渡す慣習で代替する。 関数呼び出しに名前付き引数の構文はなく、位置引数のみ。標準ライブラリではフィールドにデフォルト値を持つ`Options`のようなstructを1つの引数として受け取り、呼び出し側は型名を省略できる`.{ .field = value }`というanonymous struct literalでフィールド名を明示しつつ、省略したフィールドはデフォルト値に委ねる慣習が広く使われる。C/Goの「構造体で代替」と同じ発想だが、フィールドのデフォルト値と組み合わせられる点が異なる。 |
| OCaml | あり(labeled arguments) | foo ~width:10 ~height:20 | 併用可(ラベルなし引数と混在可) | ラベル付き引数を言語機能として持ち、省略可能なoptional引数もある。 `~label:value`でラベル付き引数を、`?label`で省略可能な引数を表現できる。ラベル付き引数同士は呼び出し順序を入れ替えられ、ラベルなしの位置引数とも混在できる。 |
| F# | あり(メンバーメソッドのみ) | obj.Foo(width = 10, height = 20) | 併用可(メンバーメソッドに限る) | .NETのメンバーメソッドでは名前付き引数が使えるが、通常のlet関数では使えない。 C#との相互運用を意識した.NETのメンバーメソッド(`member`)呼び出しでは名前付き引数が使え、位置引数と併用したり順序を入れ替えたりもできる。一方、OCaml由来のカリー化された`let`関数では、ラベル付き引数(OCamlの`~label`)に相当する機能自体がなく、位置引数のみになる。同じ言語の中で関数の定義方法によって名前付き引数の可否が変わる、やや非対称な設計。 |
| Haskell | なし | 位置引数のみ | -(レコード構文で代替) | 名前付き引数はなく、意味を明示したい場合はレコード構文を使う。 関数呼び出しに名前付き引数はない。意味を明示したい場合は、フィールド名を持つレコードを渡すことが一般的な代替手段になる。 |
| Erlang | なし(proplist/mapで代替) | foo([{width, 10}, {height, 20}]) | -(最後の引数としてまとめて受け取る) | 言語機能としての名前付き引数はなく、キーと値の組のリストやmapを最後の引数として渡す。 proplist(`{key, value}`のタプルのリスト)やmapを1つの引数として渡し、関数側で`proplists:get_value/3`やパターンマッチで値を取り出す慣習がある。`width`・`height`という名前を関数シグネチャがコンパイラソースコードを解析し、実行可能な形式(機械語や中間表現)に変換するプログラム。静的型付け言語では、実行前にコンパイラが型の不整合などをチェックする役割も担う。で検証するわけではなく、キーワードリストを言語機能として持つElixirより一段低レイヤーな表現。 |
| Elixir | なし(キーワードリストで代替) | foo(width: 10, height: 20)(実体はキーワードリスト) | -(最後の引数としてまとめて受け取る) | 言語機能としての名前付き引数はなく、最後の引数をキーワードリストとして受け取る慣習で代替する。 `width: 10, height: 20`という書き方はキーワードリストの糖衣構文であり、関数側で個別の引数名にマッピングされるわけではない。呼び出し側の見た目は名前付き引数に近いが、実体は「最後の引数として1つのリストを受け取る」設計であり、`width`・`height`という名前をコンパイラソースコードを解析し、実行可能な形式(機械語や中間表現)に変換するプログラム。静的型付け言語では、実行前にコンパイラが型の不整合などをチェックする役割も担う。が検証するわけではない。 |
| TypeScript | なし(関数引数としては) | foo({ width: 10, height: 20 }) | -(オブジェクト引数として1つにまとめる) | 名前付き引数はなく、オブジェクトを分割代入で受け取るパターンで名前を表現する。 言語機能としての名前付き引数はないが、オブジェクトを受け取り分割代入するパターンで、名前付き引数に近い書き心地を実現できる。型注釈と組み合わせれば、キーの必須・省略も表現できる。 |
| JavaScript | なし | foo({ width: 10, height: 20 }) | - | TypeScriptと同様、オブジェクトを渡して分割代入するパターンが一般的。 言語機能としての名前付き引数はなく、TypeScriptと同じくオブジェクト引数+分割代入のパターンで代替する。型がない分、キーの必須・省略はドキュメントやデフォルト値で表すしかない。 |
| PHP | あり(PHP 8.0〜) | foo(width: 10, height: 20) | 併用可 | PHP 8.0で導入。位置引数と併用でき、途中の引数だけ省略することもできる。 PHP 8.0で追加された比較的新しい機能。名前付き引数を使うと、デフォルト値を持つ引数の一部だけを省略して呼び出せる。 |
| Ruby | あり(keyword arguments) | foo(width: 10, height: 20) | 併用可(定義側で明確に区別) | キーワード引数として言語機能に組み込まれ、必須・デフォルト値ありを定義側で区別する。 Ruby 2.0でキーワード引数として導入。定義側で`width:`のように書くと必須、`height: 0`のようにデフォルト値を書くと省略可能になり、位置引数と明確に区別される。 |
| Python | あり | foo(width=10, height=20) | 併用可 | 位置引数とキーワード引数を併用でき、`*`でキーワード専用にもできる。 位置引数とキーワード引数を併用でき、`*`を使うと以降の引数をキーワード専用にできる。デフォルト引数と組み合わせて、オプションが多いAPIでもよく使われる。 |
比較の観点は次の通りである。
- 名前付き引数が言語機能として存在するか。
- 呼び出し時に名前を書くことが必須か、任意か。
- 名前付き引数と位置引数を併用できるか。
- 名前を書く場合、順序を入れ替えられるか。
- 「名前付き引数がない言語」が、構造体・オブジェクト・Builder等で何を代替しているか。
Kotlin・C#・Scala・Python・PHP・Rubyは、名前付き引数を任意の機能として提供する。デフォルト引数と組み合わせ、オプションが多い呼び出しを読みやすくする目的で使われることが多いが、名前を省略した呼び出しも許されるため、同じ関数でも呼び出し箇所によって読みやすさが変わる。
Swiftは、引数ラベルが呼び出し側に既定で現れる数少ない言語である。省略したい場合は定義側で_を明示する必要があり、「ラベルを書かない」ことのほうが特別な指定になる。OCaml・MoonBitのラベル付き引数も、ラベルを省略する構文自体を持たない。melangが目指す「必須」の設計は、この3言語に最も近い。
Rust・C++・Go・Java・Haskell・Zigは名前付き引数を持たず、意味を明示したい場合は構造体・オブジェクト(Go・Rust)やBuilderパターン(Java)、レコード構文(Haskell)で代替する。Zigはフィールドにデフォルト値を持てるstructを、型名を省略できるanonymous struct literal(.{ .field = value })で渡す慣習を持ち、省略したフィールドはデフォルト値に委ねられる。これらは、引数の意味を「引数そのものの書き方」ではなく「渡す値の型」に持たせる設計であり、melangが目指す「呼び出しのコードだけで意味が分かる」方向とは異なる。
TypeScript・JavaScriptは、オブジェクトを1つの引数として渡し、分割代入で受け取るパターンで名前付き引数に近い書き心地を実現する。Elixirのキーワードリストも見た目は似ているが、実体は「最後の引数として1つのリストを受け取る」設計であり、個々の引数名をコンパイラや処理系が検証するわけではない。
トレードオフ
名前付き引数を持たない設計(Rust, C++, Go, Java, Haskellなど)
- 利点: 呼び出しが短く書ける。引数名の変更がAPI変更として扱われない。
- 欠点: 同じ型の引数が並ぶと、位置だけでは取り違えに気づけない。意味を明示したい場合は、構造体やBuilderなど別の仕組みを都度用意する必要がある。
名前付き引数を任意で使える設計(Kotlin, C#, Scala, Python, PHP, Rubyなど)
- 利点: 必要な呼び出しだけ名前を書けば済み、簡潔な呼び出しと明示的な呼び出しを使い分けられる。デフォルト引数と組み合わせやすい。
- 欠点: 名前を書くかどうかが呼び出し側の裁量に委ねられ、同じ関数でも呼び出し箇所によって読みやすさが変わる。レビューで指摘しない限り、名前を省略した呼び出しが残り続ける。
名前付き引数を既定で必須にする設計(Swift、ラベル付き引数を持つOCaml・MoonBit)
- 利点: どの呼び出しを読んでも、引数の意味がコードだけから分かる。取り違えに呼び出し側で気づける。引数の順序を覚える必要がない。
- 欠点: 呼び出し側の記述量が増える。定義側の引数名を変えると、すべての呼び出し箇所に影響する(引数名がAPIの一部になる)。
構造体・オブジェクトで代替する設計(Rust・Go・Javaの構造体/Builder、TypeScript/JavaScriptのオブジェクト引数)
- 利点: 言語に名前付き引数の仕組みがなくても、意味を明示した呼び出しを書ける。
- 欠点: 単純な関数呼び出しのために、構造体やBuilderを別途定義する手間がかかる。オブジェクト引数は、キーの必須・省略が型(TypeScript)やドキュメント(JavaScript)に依存する。
melangの選択
melangは、Swift・OCaml・MoonBitに近く、名前付き引数を任意ではなく必須にする。定義側の引数名をそのまま呼び出し側の名前として使い、Swiftのような別ラベルは持たない。Kotlin・C#のような「任意」の設計は採用せず、位置引数だけの呼び出しを許さない。
- Pros: どの呼び出しを読んでも、引数の意味がコードだけから読み取れる。同じ型の引数が並んでいても、取り違えに呼び出し側で気づける。引数が増えたり順序が変わったりしても、呼び出し側の意図(どの値をどの引数に渡しているか)が名前で保たれる。
- Cons: 呼び出し側の記述量が、名前付き引数を任意とする言語より確実に増える。引数名の変更が、すべての呼び出し箇所に影響するAPI変更になる。単純な1引数の関数でも名前を書く必要があり、簡潔さは失われる。
意味を明示するコストよりも、呼び出しを読むだけで意味が分かることを優先する。