こんにちは、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のモジュール関数のリストを見ていて、lambda
やeval
といった多くの重要な機能がモジュール関数として実装されていることに今さらながら気が付きました。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から直接呼ぶことはできません。