戻り値型のオーバーロードが許可されないのはなぜですか?(少なくとも通常使用される言語)


9

すべてのプログラミング言語について知っているわけではありませんが、通常、戻り値の型(引数の数と型が同じであると想定)を考慮してメソッドをオーバーロードする可能性がサポートされていないことは明らかです。

私はこのようなものを意味します:

 int method1 (int num)
 {

 }
 long method1 (int num)
 {

 }

プログラミングにとって大きな問題ではありませんが、場合によっては歓迎することもありました。

明らかに、どのメソッドが呼び出されているかを区別する方法なしにそれらの言語がそれをサポートする方法はありませんが、その構文は[int] method1(num)または[long] method1(num)のような単純なものにすることができますこれにより、コンパイラーは、どちらが呼び出されるかを認識します。

コンパイラがどのように機能するかはわかりませんが、それほど難しくはないように思われるので、なぜそのようなものが通常は実装されないのでしょうか。

そのようなものがサポートされない理由はどれですか?


たぶん、あなたの質問は、2つの戻り値の型の間の暗黙的な変換が存在しない例(例:クラスFooと)を使用する方がよいでしょうBar

なぜ、そのような機能が役立つのでしょうか。
James Youngman

3
@JamesYoungman:たとえば、文字列をさまざまな型に解析するには、int read(String s)、float read(String s)などのメソッドを使用できます。メソッドのオーバーロードされたバリエーションごとに、適切なタイプの解析が実行されます。
ジョルジオ

ちなみに、これは静的に型付けされた言語の問題だけです。複数の戻り値の型を持つことは、JavascriptやPythonなどの動的型付け言語ではかなり日常的なことです。
Gort the Robot

1
@StevenBurnap、ええと、いや。例えばJavaScriptでは、関数のオーバーロードをまったく行うことができません。したがって、これは実際には関数名のオーバーロードをサポートする言語の問題のみです。
David Arno

回答:


19

型チェックが複雑になります。

引数の型に基づくオーバーロードのみを許可し、イニシャライザからの変数の型の推定のみを許可する場合、すべての型情報は一方向に流れます。構文ツリーの上方です。

var x = f();

given      f   : () -> int  [upward]
given      ()  : ()         [upward]
therefore  f() : int        [upward]
therefore  x   : int        [upward]

あなたが旅行に型情報を許可する場合は、両方のように、その使用から変数の型を推論するよう指示、あなたはタイプを決定するために(ヒンドリー-ミルナーのタイプのシステムのためのそのようなアルゴリズムWなど、)制約ソルバーを必要としています。

var x = parse("123");
print_int(x);

given      parse        : string -> T  [upward]
given      "123"        : string       [upward]
therefore  parse("123") : ∃T           [upward]
therefore  x            : ∃T           [upward]
given      print_int    : int -> ()    [upward]
therefore  print_int(x) : ()           [upward]
therefore  int -> ()    = ∃T -> ()     [downward]
therefore  ∃T           = int          [downward]
therefore  x            : int          [downward]

ここでは、の型をx未解決の型変数として残す必要∃Tがあり、解析できることだけがわかっています。場合にのみ、後に、x具体的な形で使用されている、我々は制約を解決し、その決定をするのに十分な情報持っていない∃T = int種類の情報を伝搬する、上下に呼び出し式から構文木をx

のタイプを判別できなかった場合、xこのコードもオーバーロードされるため(呼び出し元がタイプを判別する)、あいまいさに関するエラーを報告する必要があります。

これから、言語設計者は以下を結論付けることができます。

  • 実装が複雑になります。

  • これにより、型チェックが遅くなります。病理学的なケースでは、指数関数的に遅くなります。

  • 適切なエラーメッセージを生成することは困難です。

  • 現状とは違いすぎです。

  • 実装したくありません。


1
また、場合によっては、コンパイラーがプログラマーが絶対に期待していなかった選択をして、バグを見つけるのが難しくなることを理解するのは非常に難しいでしょう。
gnasher729

1
@ gnasher729:同意しない。私はこの機能を備えたHaskellを定期的に使用していますが、オーバーロード(つまり、タイプクラスインスタンス)の選択に悩まされたことはありません。何かがあいまいな場合は、型注釈を追加する必要があります。また、非常に有用であるため、私の言語に完全な型推論を実装することを選択しました。この答えは私が悪魔の擁護者を演じることでした。
Jon Purdy

4

あいまいなので。例としてC#を使用:

var foo = method(42);

どのオーバーロードを使用する必要がありますか?

わかりました、おそらくそれは少し不公平でした。仮説言語でそれを伝えないと、コンパイラーはどのタイプを使用すべきかを理解できません。だから、暗黙の型付けはあなたの言語では不可能であり、匿名の方法とそれに伴うLinqがあります...

これはどう?(要点を示すために、シグネチャを少し再定義しました。)

short method(int num) { ... }
int method(int num) { ... }

....

long foo = method(42);

intオーバーロードまたはオーバーロードを使用する必要がありshortますか?わからないので、[int] method1(num)構文で指定する必要があります。正直に言うと、解析と書き込みが少し面倒です。

long foo = [int] method(42);

実は、C#のジェネリックメソッドとは驚くほど似た構文です。

long foo = method<int>(42);

(C ++とJavaには同様の機能があります。)

つまり、言語設計者は、解析を簡素化し、より強力な言語機能を有効にするために、別の方法で問題を解決することを選択しました。

あなたはコンパイラについてよく知らないと言っています。文法とパーサーについて学ぶことを強くお勧めします。文脈自由文法が何であるかを理解すると、あいまいさがなぜ悪いことであるかについて、はるかに良い考えが得られます。


ジェネリック医薬品についての良い点は、しかし、あなたはconflatingように見えるshortint
ロバートハーヴェイ

ええ@RobertHarveyそうですね。ポイントを説明するために例を探すのに苦労していました。場合は、より良い仕事とmethod短期またはint型を返され、そして種類が長いと定義しました。
RubberDuck

それは少し良いようです。
RubberDuck

戻り値の型がオーバーロードされている言語では、型推論、匿名サブルーチン、モナド内包表記を使用できないという引数は購入しません。Haskellはそれを行い、Haskellは3つすべてを持っています。また、パラメトリックな多態性もあります。long/ int/ についてのあなたのポイントshortは、戻り値型のオーバーロードについてよりも、サブタイプ化および/または暗黙的な変換の複雑さについてです。結局のところ、C ++、Java、C♯、およびその他の多くの場合、数値リテラル戻り値の型で多重定義されており、問題はないようです。単純にルールを作成できます。たとえば、最も具体的/一般的なタイプを選択します。
イェルクWミッターク

@JörgWMittag私の要点は、それが不可能にすることではなく、物事を不必要に複雑にするだけでした。
RubberDuck

0

すべての言語機能は複雑さを増すため、すべての機能が生み出す避けられない落とし穴、稀なケース、およびユーザーの混乱を正当化するのに十分な利点を提供する必要があります。ほとんどの言語では、これは単にそれを正当化するのに十分な利点を提供しません。

ほとんどの言語では、式method1(2)には明確な型があり、多かれ少なかれ予測可能な戻り値が必要です。ただし、戻り値のオーバーロードを許可する場合、その式の一般的な意味をその周りのコンテキストを考慮せずに判断することは不可能です。unsigned long long foo()実装がで終わるメソッドがあるとどうなるか考えてみてくださいreturn method1(2)。それはlong-returningオーバーロードまたは-returningオーバーロードを呼び出すのか、intそれとも単にコンパイラエラーを与えるのか?

さらに、戻り値の型に注釈を付けることでコンパイラーを支援する必要がある場合は、より多くの構文を考案するだけでなく(機能を存在させるために前述のすべてのコストが発生します)、作成と同じことを効率的に実行できます「通常の」言語の2つの異なる名前のメソッド。である[long] method1(2)よりもより直感的なlong_method1(2)


一方、非常に強力な静的型システムを備えたHaskellのような一部の関数型言語では、このような動作が可能です。それらの型推論は、これらの言語で戻り型に注釈を付ける必要がほとんどないほど強力であるためです。しかし、それが可能なのは、これらの言語がすべての関数が純粋で参照透過であることを要求するとともに、従来の言語よりも真にタイプセーフを実施するためです。これは、ほとんどのOOP言語で実現可能なものではありません。


2
「すべての関数が純粋で参照透過であることを要求するとともに」:これにより、戻り値の型のオーバーロードがどのように簡単になりますか?
ジョルジオ

@Giorgioありません-Rustは関数の純粋さを強制せず、戻り型のオーバーロードを実行できます(Rustでのオーバーロードは他の言語とは非常に異なります(テンプレートを使用することでのみオーバーロードできます))
Idan Arye

[long]と[int]の部分には、メソッドを明示的に呼び出す方法がありました。ほとんどの場合、メソッドの呼び出し方法は、メソッドの実行が割り当てられる変数のタイプから直接推測できます。
user2638180

0

これ Swift 利用可能であり、そこで正常に動作します。明らかに、両側にあいまいなタイプを設定することはできないため、左側で認識される必要があります。

これを単純なエンコード/デコードAPIで使用しました

public protocol HierDecoder {
  func dech() throws -> String
  func dech() throws -> Int
  func dech() throws -> Bool

つまりinit、オブジェクトのなど、パラメーターの型がわかっている呼び出しは、非常に簡単に機能します。

    private static let typeCode = "ds"
    static func registerFactory() {
        HierCodableFactories.Register(key:typeCode) {
            (from) -> HierCodable in
            return try tgDrawStyle(strokeColor:from.dech(), fillColor:from.dechOpt(), lineWidth:from.dech(), glowWidth: from.dech())
        }
    }
    func typeKey() -> String { return tgDrawStyle.typeCode }
    func encode(to:HierEncoder) {
        to.ench(strokeColor)
        to.enchOpt(fillColor)
        to.ench(lineWidth)
        to.ench(glowWidth)
    }

細心の注意を払っている場合、dechOpt上記の呼び出しに気付くでしょう。呼び出し側のコンテキストがオプションであるという期待を導入できるため、微分器がオプションを返す同じ関数名をオーバーロードするとエラーが発生しやすくなるという難しい方法を見つけました。


-5
int main() {
    auto var1 = method1(1);
}

その場合、コンパイラはa)あいまいであるため呼び出しを拒否するb)最初/最後の1つを選択するc)のタイプを残し、タイプのvar1推論を続行し、他の式var1が選択のタイプを決定するとすぐに、正しい実装。一番下の行で、型推論が自明ではない場合を示すことは、その型推論が一般に自明でないこと以外の点を証明することはめったにありません。
back2dos

1
強い議論ではありません。たとえば、Rustは型推論を頻繁に使用し、場合によっては、特にジェネリックでは、必要な型を特定できないことがあります。このような場合、型推論に依存する代わりに、明示的に型を指定する必要があります。
8ビットツリー2016

1
ええと...それはオーバーロードされた戻り値の型を示していません。 method1特定の型を返すように宣言する必要があります。
Gort the Robot
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.