Ruby
プロを目指す人のためのRuby入門

Re: Rubyの関数とメソッドの違いってなんだろ!


はじめに

先日、こちらのツイートを拝見しました。

そのままTwitterでリプライを返そうとしたのですが、文字数が足りないのでQiitaに書くことにしました。


TL; DR(最初に結論)

結構長い記事になってしまったので、最初に結論をまとめておきます。


  • Rubyはあらゆるものがオブジェクトなので、本来は全部メソッドと呼ぶ方が適切

  • ただし、一般的なプログラミング用語として、関数とメソッドは近しい存在なので、混同して使われることも多い(用語の使い分けにあまり神経質にならない方が良い)

  • クラスの外で定義したメソッドやputsなどは、他の言語における「(組み込み)関数」のように使えるが、これもやはり実際はメソッド

それでは以下が本編です。


一般論としての「メソッド」と「関数」の違い

まず、用語の定義を確認しておきましょう。

一般的なプログラミング用語としての「関数」はIT用語辞典の説明が詳しいです。

関数(ファンクション)とは - IT用語辞典 e-Words

雑にまとめると、「関数とは、何かしらの処理をひとまとめにしたもので、引数を受け取ったり、戻り値を返したりする(引数や戻り値がないものもある)」という感じでしょうか。

(言語によっては戻り値がないものは関数ではなく、「サブルーチン」や「プロシージャ」と呼んで区別するものもありますが、ここではその話題には触れません)

続いて、「メソッド」ですが、これはMDNの次の説明が一番しっくりきました。


メソッドとはあるオブジェクトに結びつけられた関数のことです。

メソッドの定義 - JavaScript | MDN より


上の説明に「オブジェクト」という用語が出てきていることからわかるように、「メソッド」はオブジェクト指向プログラミングの用語として使われることが多いです。

すなわち、オブジェクト指向プログラミングのコンテキストにおいては、「オブジェクトの関数」と呼ぶより、「オブジェクトのメソッド」と呼ぶことの方が多くなります。


Rubyはすべてがオブジェクト = すべてがメソッド

ここで話題をRubyに戻します。

Rubyは「すべてがオブジェクト」として設計されている純粋なオブジェクト指向言語です。


Ruby においては整数や文字列なども含めデータ型はすべてがオブジェクトであり、純粋なオブジェクト指向言語といえる。

Ruby - Wikipedia より


ということは、Rubyに出てくるものは関数ではなく、すべてメソッドと呼ぶのが正しい、ということになります。


とはいえ、「関数」と呼んでもそこまで変ではない

が、一般的なプログラミング用語として、関数とメソッドはかなり近しい存在なので、そこまで厳密に呼び分ける必要もないと僕は思っています。

実際、開発の現場では「関数」と「メソッド」が混同されることも多いです。

他のRubyプログラマが「この関数の実装なんだけど・・・」と言ってきたときに、「は?関数?メソッドじゃないんですか??」みたいなことを指摘すると、相手の反感を買うかもしれません。

ですので、そういうときは「ふんふん、つまりメソッドのことね」と心の中でつぶやくだけに留めておきましょう。


クラスの内と外で呼び方が違う?

冒頭に挙げたツイートでは、関数とメソッドの違いが「クラスの内外による差」にあるのでは、というようなことが書かれていました。

この件はもしかすると、以下の説明のことを言っておられるのかもしれません。


トップレベル ならばどこからでも呼べるメソッドを定義します。このようなメソッドは結果 として他の言語における「関数」のように使えます。

クラス/メソッドの定義 (Ruby 2.6.0) より


Ruby以外の言語では、最初からいつでもどこでも使える「組み込み関数」が用意されているものがあります。

たとえば、Pythonにはabsという絶対値を返す組み込み関数が用意されています。

# absは絶対値を返す組み込み関数

abs(-1)
#=> 1

class Foo:
def bar(self):
# クラスの内でも外でもabsが使える
return abs(-1)

foo = Foo()
foo.bar()
#=> 1

上のPythonのコードのように、「いつでもどこでも使えるabs」をRubyで定義する場合は以下のようになります。

# いきなりabsを呼ぶと未定義なのでエラーになる

abs(-1)
#=> NoMethodError: undefined method `abs' for main:Object

# トップレベルにabsを定義する
def abs(x)
x.abs
end

# クラスの外でも中でもabsが呼べるようになる
abs(-1)
#=> 1

class Foo
def bar
abs(-1)
end
end

foo = Foo.new()
foo.bar
#=> 1

上のコード例のように、Rubyではクラスの外でメソッドを定義すると、他の言語でいうところの「組み込み関数」のようにそのメソッドが使えるようになります。

そのため、もしかすると、「クラスの内側ではメソッド、外側では関数」というように呼び分けるケースがあるのかもしれません(僕はわざわざ呼び分けることはないですが)。


トップレベルって何?上で書いたabsは関数じゃないの?

上ではさらっと「トップレベル」という用語が出てきました。

この内容にもついて軽く説明しておきます。

クラス構文の外側の世界をRubyでは「トップレベル」と呼びます。

トップレベルのselfはmainという名前のObjectクラスのインスタンスです。

self

#=> main

self.class
#=> Object

そして、トップレベルで定義したメソッドは暗黙的にObjectクラスのprivateメソッドになります。

先ほど定義したabsも、Objectクラスのprivateメソッドなので、関数というよりもメソッドと呼ぶ方が適切です。

# absはmainオブジェクトのメソッドとして定義されている

method(:abs)
=> #<Method: Object#abs>

さらに、あらゆるオブジェクトはObjectクラスを継承しているため、Objectクラスのprivateメソッドはどのクラスの内部でも呼び出せるメソッドになります。

Fooクラスの内部でabsメソッドが呼び出せるのもそのためです。

ちなみに、このあたりの内容は拙著「プロを目指す人のためのRuby入門」の第8章でも詳しく説明しています。


putsは組み込み関数じゃないの?

Ruby初学者にはお馴染みのメソッドとしてputsがあります。

そう、Rubyで"Hello, world!"するときに使うアレです。

# トップレベルでputsを呼び出す

puts 'Hello, world!'
#=> Hello, world!

Pythonのabs関数のように、クラスの内部で外部でも使えるので、一見、putsも組み込み関数のように見えます。

class Foo

def bar
# クラスの内部でputsを使うこともできる
puts 'Hello, world!'
end
end

foo = Foo.new
foo.bar
#=> Hello, world!

しかし、これもやはりメソッドです。

method(:puts)

#=> #<Method: Object(Kernel)#puts>

putsはKernelモジュールに定義されているメソッドです。1

module function Kernel.#puts (Ruby 2.6.0)

そしてKernelモジュールはObjectクラスにincludeされています。

Object.include?(Kernel)

#=> true

そのため、トップレベルのmainオブジェクトや、(Objectクラスを暗黙的に継承している)クラスの内部でputsが使えるようになっています。

このあたりの内容も拙著「プロを目指す人のためのRuby入門」の第8章で詳しく説明しています。


まとめ

今回のエントリをまとめると次のようになります。


  • Rubyはあらゆるものがオブジェクトなので、本来は全部メソッドと呼ぶ方が適切

  • ただし、一般的なプログラミング用語として、関数とメソッドは近しい存在なので、混同して使われることも多い(用語の使い分けにあまり神経質にならない方が良い)

  • クラスの外で定義したメソッドやputsなどは、他の言語における「(組み込み)関数」のように使えるが、これもやはり実際はメソッド

とういわけで、こんな感じで関数とメソッドの違いを理解しておけば大丈夫じゃないかな〜と僕は考えています。





  1. 厳密にいうとputsはKernelモジュールの「モジュール関数」です。メソッドだと説明しているのに「関数」という用語を使うとややこしいので、あえてここでは「メソッド」と表現しました。モジュール関数についてはネットの記事や、拙著「プロを目指す人のためのRuby入門」の8.7.2項を参照してください。