やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

「オブジェクト」「クラス」を読む1

 書いている途中って感じ。これはひどい

成果物

情報源

ドキュメント

 以下の項目において概要説明があった。まだ書いている途中らしく、説明がなく見出しだけの項もあった。

  • オブジェクト
  • クラス
  • モジュール
  • メソッド
  • 特異クラス
  • 特異メソッド

オブジェクト

 オブジェクトとはインスタンスのこと。特定のクラスからnewされたメモリ実体。メンバ変数とメソッドを持つ。

 Rubyではすべてがオブジェクトである。Javaは数値や真偽値のリテラルがプリミティブ型でありオブジェクトではない。なので数値はメソッドをもっていない。Rubyは数値リテラルさえもオブジェクトであるため、メソッドをもっている。

3.times {|i| p i}

すべてがオブジェクトである

 公式ドキュメントのオブジェクトには以下のように書いてあった。

Ruby で扱える全ての値はオブジェクトです。

 たぶん上記が答えだと思う。値というのが何を指しているのかわからないが。リテラル、シンボル、変数、定数のことかな? それ以外のクラス、モジュール、メソッドなどは値なんだろうか。よくわからない。

 以下全文。

Ruby で扱える全ての値はオブジェクトです。 Ruby のオブジェクトに対して可能な操作はメソッド呼び出しのみです。あるオブジェクトが反応できるメソッドは、そのオブジェクトが所属するクラスによって一意に決定します。所属するクラスはオブジェクト生成時に決まり、その後は特異クラスの導入以外、所属クラスが変わることはありません。またオブジェクトは、特定のクラスに対比して、その「インスタンス」とも呼ばれます。

オブジェクトの生成は、一般には、別のオブジェクトのメソッドを呼び出すことによって行われます。

 以下、非公式のドキュメント。

メソッドやブロックはオブジェクトではありません。

変数・定数も演算子も制御構造もデータではないので,オブジェクトではありません。

プログラムもそれ自体はオブジェクトではありません。

Ruby はすべてのデータがオブジェクト」と表現すべきでしょう。

 じゃあデータって何? 変数に代入できるものをデータと呼んでいるのかな? でもそれだと変数・定数はデータになるし、制御構造だって、すべては式であり値を返すのだから代入できる。まあ式自体が代入されるわけではないけれど。データの定義がよくわからなかった。

 たぶん「すべてがオブジェクト」というのは既存のC言語Javaと比べたときの表現なのだろう。数値型などのリテラルはオブジェクトでなくプリミティブだったので、その対比として「すべて」と言っただけなんだろうな。そもそもドキュメントには「すべての値」と書いてあり「すべて」ではないし。

 「Rubyは数値、真偽値、文字列などのリテラル値でさえオブジェクトです」といったほうがいいということだろうか。

 ところでdefで定義されたメソッドって、本当にオブジェクトじゃないの? 以下で調べたらオブジェクトっぽいんだけど。

ri Method
= Method < Object

(from ruby core)
------------------------------------------------------------------------
Method objects are created by Object#method, and are associated with a
particular object (not just with a class).  They may be used to invoke
the method within the object, and as a block associated with an
iterator.  They may also be unbound from one object (creating an
UnboundMethod) and bound to another.

 おもいっきりMethod objectsって書いてますやん。

ri method
Looks up the named method as a receiver in obj, returning a Method
object (or raising NameError). The Method object acts as a closure in
obj's object instance, so instance variables and the value of self
remain available.

 ここでもMethod objectって書いてあるぞ。

 メソッドもオブジェクトなんじゃないの? 上記の記事のいうことが嘘くさい。公式ドキュメントじゃないしな。

 この分だとブロックもメソッドなんじゃない? たしか公式ドキュメントには「Ruby演算子はすべて糖衣構文です」みたいなことが書いてあった。もしやRubyのブロックもProcオブジェクトの糖衣構文だったりしない? 知らんけど。

ri Object

ri Object
= Object < BasicObject

------------------------------------------------------------------------
= Includes:
Kernel (from ruby core)

(from ruby core)
------------------------------------------------------------------------





https://github.com/seattlerb/minitest/blob/13c48a03d84a2a87855a4de0c959f
96800100357/lib/minitest/mock.rb#L192


Object is the default root of all Ruby objects.  Object inherits from
BasicObject which allows creating alternate object hierarchies.  Methods
on Object are available to all classes unless explicitly overridden.

Object mixes in the Kernel module, making the built-in kernel functions
globally accessible.  Although the instance methods of Object are
defined by the Kernel module, we have chosen to document them here for
clarity.

When referencing constants in classes inheriting from Object you do not
need to use the full namespace.  For example, referencing File inside
YourClass will find the top-level File class.

In the descriptions of Object's methods, the parameter
symbol refers to a symbol, which is either a quoted string
or a Symbol (such as :name).
------------------------------------------------------------------------
= Constants:

ARGF:
  ARGF is a stream designed for use in scripts that process files given
  as command-line arguments or passed in via STDIN.

  See ARGF (the class) for more details.

ARGV:
  ARGV contains the command line arguments used to run ruby.

  A library like OptionParser can be used to process command-line
  arguments.

Bignum:
  An obsolete class, use Integer


CROSS_COMPILING:
  [not documented]

DATA:
  DATA is a File that contains the data section of the executed file. To
  create a data section use __END__:

    $ cat t.rb
    puts DATA.gets
    __END__
    hello world!

    $ ruby t.rb
    hello world!

ENV:
  ENV is a Hash-like accessor for environment variables.

  See ENV (the class) for more details.

Fixnum:
  An obsolete class, use Integer


ParseError:
  [not documented]

RUBY_COPYRIGHT:
  The copyright string for ruby

RUBY_DESCRIPTION:
  The full ruby version string, like ruby -v prints

RUBY_ENGINE:
  The engine or interpreter this ruby uses.

RUBY_ENGINE_VERSION:
  The version of the engine or interpreter this ruby uses.

RUBY_PATCHLEVEL:
  The patchlevel for this ruby.  If this is a development build of ruby
  the patchlevel will be -1

RUBY_PLATFORM:
  The platform for this ruby

RUBY_RELEASE_DATE:
  The date this ruby was released

RUBY_REVISION:
  The GIT commit hash for this ruby.

RUBY_VERSION:
  The running version of ruby

Readline:
  [not documented]

STDERR:
  Holds the original stderr


STDIN:
  Holds the original stdin


STDOUT:
  Holds the original stdout


TOPLEVEL_BINDING:
  The Binding of the top level scope

TimeoutError:
  Raised by Timeout.timeout when the block times out.


= Class methods:

  yaml_tag

= Instance methods:

  !~, <=>, ===, =~, CSV, DelegateClass, Digest, define_singleton_method,
  display, dup, enum_for, eql?, extend, freeze, hash, inspect,
  instance_of?, instance_variable_defined?, instance_variable_get,
  instance_variable_set, instance_variables, is_a?, itself, kind_of?,
  method, methods, nil?, object_id, private_methods, protected_methods,
  public_method, public_methods, public_send, remove_instance_variable,
  respond_to?, respond_to_missing?, send, singleton_class,
  singleton_method, singleton_methods, stub, taint, tainted?, timeout,
  to_enum, to_s, to_yaml, trust, untaint, untrust, untrusted?, xmp
オブジェクトは、すべてのRubyオブジェクトのデフォルトのルートです。 オブジェクトはから継承します
代替オブジェクト階層の作成を可能にするBasicObject。 メソッド
on Objectは、明示的にオーバーライドされない限り、すべてのクラスで使用できます。

カーネルモジュールでオブジェクトが混在し、組み込みのカーネル機能が作成されます
グローバルにアクセス可能。 Objectのインスタンスメソッドは
カーネルモジュールによって定義されているため、ここでそれらを文書化することを選択しました。
明快さ。

Objectから継承するクラスの定数を参照する場合、
完全な名前空間を使用する必要があります。 たとえば、内部のファイルを参照する
YourClassは、最上位のFileクラスを検索します。

オブジェクトのメソッドの説明では、パラメータ
記号は、引用符で囲まれた文字列のいずれかである記号を指します
またはシンボル(:nameなど)。

 Objectクラスの親にBasicObjectというのがあるらしい。

 そもそもオブジェクトといっているのはObjectクラスやそのインスタンスのことを指していたのだろうか? それとももっと一般的が概念として、メンバ変数とメソッドを持っているものとしての意味で言ったのだろうか。わからん。

クラス

クラスは自身に所属するオブジェクトが反応できるメソッドを決定します。所属するオブジェクトに対してあるメソッドを呼び出すことができるなら、そのメソッドが「クラスに定義されている」と言います。またメソッドはクラスとメソッド名によって一意に決定します。

クラスは必ずただひとつの「スーパークラス」を持ち、スーパークラスであるメソッドが定義されていれば自クラスでもそのメソッドが同じ内容で定義されていることになります。これを「継承」と呼びます。継承は推移するので、スーパークラススーパークラスが持つメソッドもまた自クラスにおいて定義されていることになります。

 この説明はたぶんJavaC#などのちゃんとしたオブジェクト指向言語と似たようなものだと思う。

モジュール

include

ちょっと特殊でクラスではない。しかし機能は同じ。

 急に雑になった。

 機能はおなじ? 嘘でしょ? newできるの? 継承できるの?

 irbで確かめたが、継承もnewもできない。嘘じゃん。これはひどい。そのことをクラスではないの一言で説明したつもりってこと? いやいや伝わらないから。

module M; end
module N < M; end
: syntax error, unexpected '<' (SyntaxError)
> M.new
: undefined method `new' for M:Module (NoMethodError)

 じゃあモジュールってなんなの? 機能は同じっていうけど、すでに違うんですけど? ていうか機能ってなにを指しているの? 同じ部分があるなら違う部分もあるはずだよね? だってすべてが同じだったらクラスだけでいいし。モジュールというクラスとは違う概念をわざわざ用意したんだからクラスとは別物のはず。実際、クラスではないって言ってるし。

 なんのためにモジュールを作ったの? どうやって使うの? 用途は? なんの説明もないのでさっぱりわからん。

 ググってみると以下がヒットした。

 以下のように使うらしい。

  • 名前空間として使う
  • クラスへMixinして多重継承する

 DRYに書くための存在だと思われる。異なるクラス間をまたいだ共通処理をモジュールに書いて各クラスへ取り込む。そういったことをするのがモジュールの肝なのだろう。

 名前空間として使うのも大切だろう。名前重複はコードが肥大化すると避けられないからね。

 使い方を網羅するのはちょっと大変そう。また別口で勉強しよう。今回はモジュールの用途だけを把握する感じでいい。

 仕組みとしては継承チェーンに含まれることになるようだ。

メソッド

メソッドは実行することができます。その実行を開始することを通常「呼び出す」と言います。また呼び出すときにはオブジェクトを渡すことができ、そのオブジェクトを「引数」と呼びます。いくつの引数を受け取ることができるかはメソッドの定義時に決定し、変更することはできません。

 ふつうは最初に「処理のまとまり」とか「関数」という説明をすると思うのだが。1つ以上の式や文をまとめたものとか。引数には言及しているのに戻り値の説明がないし。なんかもう超適当だな。

クラスとオブジェクト

Ruby プログラムからはクラスもまたオブジェクトとして扱うことができ、文による操作と合わせると以下の基本操作が可能です。

  • メソッドの存在を問い合わせる
  • メソッドを定義する
  • メソッドの定義を取り消す

特異メソッド

 1つのインスタンスに固有のメソッド。既存のメソッドを上書きする形で定義する。存在しない名前のメソッドを特異メソッドとして作ることはできない。

class C
  def m; p 'm'; end
end
c = C.new
d = C.new
c.m
d.m
def d.m; p 'M'; end
c.m
d.m

 def d.m; ...が特異メソッドの定義。これによりインスタンスdだけはMを返すようになる。

 Rubyは特異メソッドが定義されると、特異クラスを生成して、継承チェーンに組み込む。

 継承チェーンは以下が参考になった。

特異クラス

 特異クラスはObject#singleton_classによって確認できる。この特異クラスに定義されたものが特異メソッドである。特異クラスは元のクラスより継承チェーンの前方に来るため優先度が高い。よって特異メソッドが定義されたインスタンスは、通常のインスタンスメソッドよりも特異メソッドが優先されて呼び出される。

class C
  class << self
    def m; :M; end
  end
end
class << C
  def m; :N; end
end

 上記URLを読むと「クラスメソッドは特異メソッドである」と言っているように見えるのだが。だって特異メソッドとクラスメソッドとextendの3つの記法は、おなじ「クラスメソッドの定義」という見出しの中で紹介されている。つまり特異メソッドもクラスメソッドもextendも同じってことでは?

 Object#singleton_classにメソッドをもたせる方法は以下の3つ。それらはクラスメソッドとして機能する。そういう解釈でよろしいか?

  • クラスメソッド
  • 特異メソッド
  • extend

 Object#singleton_classは特異クラスであり、すべてのオブジェクトが1つだけ持てる特殊なインスタンスである。まさにsingletonの名にふさわしい。ところでインスタンスごとに1つではなく、クラスごとに1つである。それはクラスメソッドと同じではないだろうか。

 でもちょっと待って。特異クラスが持っているのは特異メソッドでしょ? でも特異メソッドって特定のインスタンスだけがもつメソッドなんだよね?

 あ、そうか。Rubyではすべてがオブジェクト。つまりクラスもオブジェクトだし、インスタンスもオブジェクトだ。そして特異クラスとは、すべてのオブジェクトが1つずつ持っているもの。つまりクラスも特異クラスを持っているし、インスタンスも特異クラスを持っている。で、クラスAがもつ特異クラスに特異メソッドを定義したら、クラスメソッドになる。それに対し、クラスAインスタンスaに対して、aがもつ特異クラスに特異メソッドを定義したら、インスタンスaの固有メソッドになる。そういうことか!

 まとめる。特異クラスは全オブジェクトが1つだけ持てるObject#singleton_classである。以下3つの特異クラスははそれぞれ異なるものである。

class A; end
a1 = A.new
a2 = A.new
A.singleton_class
a1.singleton_class
a2.singleton_class

 クラスがもつ特異クラスに特異メソッドを定義する記法は次のとおり。

class A
  def A.m; :cm; end
end

class B
  def self.m; :cm; end
end

class C; def self.m; :cm; end; end
class << C
  def m; :CM; end
end

 インスタンスがもつ特異クラスに特異メソッドを定義する記法は次の通り。

class C
  def m; p 'm'; end
end
c = C.new
d = C.new
def d.m; p 'M'; end

 なお、クラスがもつ特異クラスに特異メソッドを定義することは、ほかのプログラミング言語におけるクラスメソッドと同じである。なぜならそのクラスにひとつだけしか存在しないから。

 インスタンスがもつ特異クラスに特異メソッドを定義することは、ほかのプログラミング言語における継承と似ている。ただし継承とは別物だ。Rubyは継承リストによって呼び出すメソッドを決定している。その仕組みを理解することが正しい。Module#ancestors参照。

所感

 特異クラス、特異メソッド、extendあたりが怪しい。とくにextendについて。

  • どんな記述方法があるのか
  • その効果に違いはあるのか

 まさかと思うが、includeprependについてもメソッドがあって、class内に書いたときと動作が違ったりするんじゃないの?

 もう疲れたよパトラッシュ。たのむから断片的な情報でなくまとめてくれや。ググッて断片情報みつけて試してこれまでのルールぶち壊されての繰り返しとか、勘弁して。ちゃんと勉強させてよ公式ドキュメント。