Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

[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] module_functionでモジュールの特異メソッドを簡潔に書く


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。