[Ruby] Kernelのモジュール関数について

こんにちは、hachi8833です。

Rubyスタイルガイドを読む: 文法(8)で取り上げたKernelモジュールとモジュール関数が気になったのでもう少し追ってみました。

条件

  • Ruby: 2.4

Kernelモジュールについて

全てのクラスから参照できるメソッドを定義しているモジュール。 Object クラスはこのモジュールをインクルードしています。
Object クラスのメソッドは実際にはこのモジュールで定義されています。これはトップレベルでのメソッドの再定義に対応するためです。
Rubyリファレンスマニュアル: module Kernelより

RubyのObjectクラスのメソッドのほとんどは、実際にはKernelモジュールをインクルードすることで実現しています。

Kernelのモジュール関数について

Rubyリファレンスマニュアルでは、上述のKernelモジュールのメソッドを「モジュール関数」と呼んでいます。

モジュール関数についてはTechRacho記事「[Ruby] module_functionでモジュールの特異メソッドを簡潔に書く」で解説したように「プライベートメソッドであると同時に モジュールの特異メソッドでもあるようなメソッド」です。

KernelモジュールがObjectクラスにインクルードされることで、(BasicObjectを除くすべてのオブジェクトでKernelのモジュール関数を利用できるようになります。

モジュール関数はKernel.をつけてもつけなくても実行できます。#newは不要です。

Kernelのモジュール関数のリストを見ていて、lambdaevalといった多くの重要な機能がモジュール関数として実装されていることに今さらながら気が付きました。Rubyのいわゆる組み込みメソッドの多くがこの方法で実装されているということですね。

Objectクラスのモジュール関数のリスト

Objectクラスにインクルードされているモジュール関数のリストを見てみたくてあれこれやっていたところ、babaさんが以下のコードで取れると教えてくれました。モジュール関数はprivateなので、#private_methodsで取る必要があったのでした。

Object.private_methods.each{|m| p Object.method(m)}
#<Method: Class#initialize>
#<Method: Class#inherited>
#<Method: Class(Module)#using>
#<Method: Class(Module)#attr>
#<Method: Class(Module)#attr_reader>
#<Method: Class(Module)#attr_writer>
#<Method: Class(Module)#attr_accessor>
#<Method: Class(Module)#remove_const>
#<Method: Class(Module)#remove_method>
#<Method: Class(Module)#method_added>
#<Method: Class(Module)#method_removed>
#<Method: Class(Module)#protected>
#<Method: Class(Module)#method_undefined>
#<Method: Class(Module)#undef_method>
#<Method: Class(Module)#public>
#<Method: Class(Module)#private>
#<Method: Class(Module)#initialize_copy>
#<Method: Class(Module)#initialize_clone>
#<Method: Class(Module)#alias_method>
#<Method: Class(Module)#included>
#<Method: Class(Module)#extended>
#<Method: Class(Module)#prepended>
#<Method: Class(Module)#define_method>
#<Method: Class(Object)#log>
#<Method: Class(Object)#DelegateClass>
#<Method: Class(Object)#Digest>
#<Method: Class(Object)#P>
#<Method: Class(Kernel)#sprintf>
#<Method: Class(Kernel)#format>
#<Method: Class(Kernel)#Integer>
#<Method: Class(Kernel)#Float>
#<Method: Class(Kernel)#String>
#<Method: Class(Kernel)#Array>
#<Method: Class(Kernel)#Hash>
#<Method: Class(Kernel)#fail>
#<Method: Class(Kernel)#iterator?>
#<Method: Class(Kernel)#__method__>
#<Method: Class(Kernel)#catch>
#<Method: Class(Kernel)#__dir__>
#<Method: Class(Kernel)#loop>
#<Method: Class(Kernel)#global_variables>
#<Method: Class(Kernel)#throw>
#<Method: Class(Kernel)#block_given?>
#<Method: Class(Kernel)#raise>
#<Method: Class(Kernel)#__callee__>
#<Method: Class(Kernel)#eval>
#<Method: Class(Kernel)#Rational>
#<Method: Class(Kernel)#Pathname>
#<Method: Class(Kernel)#trace_var>
#<Method: Class(Kernel)#untrace_var>
#<Method: Class(Kernel)#at_exit>
#<Method: Class(Kernel)#Complex>
#<Method: Class(Kernel)#set_trace_func>
#<Method: Class(Kernel)#gem>
#<Method: Class(Kernel)#select>
#<Method: Class(Kernel)#caller>
#<Method: Class(Kernel)#caller_locations>
#<Method: Class(Kernel)#`>
#<Method: Class(Kernel)#test>
#<Method: Class(Kernel)#fork>
#<Method: Class(Kernel)#exit>
#<Method: Class(Kernel)#sleep>
#<Method: Class(Kernel)#respond_to_missing?>
#<Method: Class(Kernel)#gem_original_require(require)>
#<Method: Class(Kernel)#load>
#<Method: Class(Kernel)#pp>
#<Method: Class(Kernel)#exec>
#<Method: Class(Kernel)#exit!>
#<Method: Class(Kernel)#system>
#<Method: Class(Kernel)#spawn>
#<Method: Class(Kernel)#abort>
#<Method: Class(Kernel)#syscall>
#<Method: Class(Kernel)#open>
#<Method: Class(Kernel)#printf>
#<Method: Class(Kernel)#print>
#<Method: Class(Kernel)#putc>
#<Method: Class(Kernel)#puts>
#<Method: Class(Kernel)#gets>
#<Method: Class(Kernel)#readlines>
#<Method: Class(Kernel)#readline>
#<Method: Class(Kernel)#initialize_dup>
#<Method: Class(Kernel)#rand>
#<Method: Class(Kernel)#p>
#<Method: Class(Kernel)#srand>
#<Method: Class(Kernel)#proc>
#<Method: Class(Kernel)#lambda>
#<Method: Class(Kernel)#trap>
#<Method: Class(Kernel)#require>
#<Method: Class(Kernel)#require_relative>
#<Method: Class(Kernel)#binding>
#<Method: Class(Kernel)#local_variables>
#<Method: Class(Kernel)#warn>
#<Method: Class(BasicObject)#method_missing>
#<Method: Class(BasicObject)#singleton_method_added>
#<Method: Class(BasicObject)#singleton_method_removed>
#<Method: Class(BasicObject)#singleton_method_undefined>

モジュールやBasicObjectクラスのメソッドを除くと、Objectクラス自身のメソッドは非常に少ないことがわかりました。

参考: Rubyの「関数」について

Rubyリファレンスマニュアルの片隅にひっそりと関数一覧というものがあります。なぜか目次にエントリがありません。

Rubyのモジュール関数を検索しているとときどきこの関数一覧にたどり着いてしまうので、若干まぎらわしいですね。

いくつか開いてみるとわかるように、これらはRubyの内部で使用されるC言語などの関数です。ALLOCのような大文字の関数はCのマクロでした。

これらはRubyから直接呼ぶことはできません。

関連記事

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833

コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。
これまでにRuby on Rails チュートリアル第2版の半分ほど、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れてそれぞれ一部を翻訳。
かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。
実は最近Go言語が好き。
仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

BPSアドベントカレンダー

週刊Railsウォッチ

インフラ

BigBinary記事より

ActiveSupport探訪シリーズ