Juliaとオブジェクト指向

[Pythonのオブジェクト指向プログラミングを完全理解](https://qiita.com/kaitolucifer/items/926ed9bc08426ad8e835)という記事はオブジェクト指向の歴史などが書いてあり、とても勉強になります。この記事の前半部分で、オブジェクト指向とプロセス指向の書き方について違いを述べていまして、「オブジェクト指向の方が簡潔に記述できる」というようなことが書かれています。
ところで、Juliaはオブジェクト指向を採用しておりませんが、多重ディスパッチによって同等の機能を実現していると言われています。これはつまり、「プロセス指向」であるにもかかわらずこの記事に書かれているようなことができる、はずです。そこで、Pythonのコードと比較をしながらJuliaでの書き方についてみていきましょう。

## 2-1. インターフェースの統一と管理
「2-1. インターフェースの統一と管理」の部分のPythonコードをこちらに再掲しますと、鳥、犬、魚のクラスを
```python
class Bird:
    def __init__(self, name):
        self.name = name

    def move(self):
        print("The bird named {} is flying".format(self.name))


class Dog:
    def __init__(self, name):
        self.name = name

    def move(self):
        print("The dog named {} is running".format(self.name))


class Fish:
    def __init__(self, name):
        self.name = name

    def move(self):
        print("The fish named {} is swimming".format(self.name))
```
と作成し、
```python
bob = Bird("Bob")
john = Bird("John")
david = Dog("David")
fabian = Fish("Fabian")
```
とインスタンスを作成しています。そして```move```メソッドを呼び出していました：
```
bob.move()
john.move()
david.move()
fabian.move()
```
記事では、プロセス指向の場合は

>　bobというオブジェクトが来たら、それが「鳥」なのか「犬」なのかをまず明確にしないと、move_birdとmove_dogのどれにするかが決められません。実際のプログラムではmoveだけではなく、数十種類の処理関数を実装するのが普通です。関数が多くなると、変数との対応関係を明確にするのが極めて難しくなります。また、これらの関数は内部で他の関数を呼び出している可能性もあり、この関数を他のプログラムで再利用する時に、内部で使われている関数も全部見つけ出して、移行する必要があります。
　オブジェクト指向は変数を使って、classからインスタンスを作成し、どのメソッドが使えるかはclassを見れば分かります。そして、classとして抽象化することで、同じ文脈の関数が固まり、管理しやすくなります。

とあります。これをJuliaでやってみます。鳥と犬と魚の構造体を
```julia

struct Bird
    name
end
function move(self::Bird)
    println("The bird named $(self.name) is flying")
end

struct Dog
    name
end
function move(self::Dog)
    println("The dog named $(self.name) is running")
end

struct Fish
    name
end
function move(self::Fish)
    println("The fish named $(self.name) is swimming")
end
```
と定義します。インスタンスは
```julia
bob = Bird("Bob")
john = Bird("John")
david = Dog("David")
fabian = Fish("Fabian")
```
で作成されます。そして、```move```は
```julia
move(bob)
move(john)
move(david)
move(fabian)
```
となります。出力結果はPythonのコードと同じく、
```
The bird named Bob is flying
The bird named John is flying
The dog named David is running
The fish named Fabian is swimming
```
となります。全て同じ```move```で動作を決めており、
>bobというオブジェクトが来たら、それが「鳥」なのか「犬」なのかをまず明確にしないと、move_birdとmove_dogのどれにするかが決められません。

の部分をJuliaは多重ディスパッチによって解消していることがわかります。型が異なれば同じ名前でも異なるメソッドが呼ばれており、これが多重ディスパッチの機能です。

なお、Juliaで書く場合には
```julia
struct Dog{T}
    name::T
end
```
とした方がパフォーマンスとしてはよいですが、わかりやすさのため、この記事では基本的には型情報は書いていません。


## 2-2. カプセル化
「2-2. カプセル化」についてみていきます。元の記事のPythonコードは
```python
class Person:
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height

    def describe(self):
        print("name: {}; age: {}; height: {}".format(self.name, self.age, self.height))

    def introduce(self):
        print("My name is {}, and height is {}, and age is {}. ".format(self.name, self.height, self.age))


bob = Person("Bob", 24, 170)
mary = Person("Mary", 10, 160)
bob.describe()
bob.introduce()
mary.describe()
mary.introduce()
```
です。
> オブジェクト指向は関数とデータを一緒に束ねてくれるので、同じ変数（データ）をたくさんの関数で処理したい時はとても便利です。

とのことでした。
これをJuliaでやってみます。
```julia
struct Person
    name
    age
    height
end
describe(self::Person) = println("name: $(self.name); age: $(self.age); height: $(self.height)")
introduce(self::Person) = println("My name is $(self.name), and height is $(self.height), and age is $(self.age). ") 

bob = Person("Bob", 24, 170)
mary = Person("Mary", 10, 160)
describe(bob)
introduce(bob)
describe(mary)
introduce(mary)
```
となります。出力結果は
```
name: Bob; age: 24; height: 170
My name is Bob, and height is 170, and age is 24. 
name: Mary; age: 10; height: 160
My name is Mary, and height is 160, and age is 10. 
```
です。
ほとんどPythonと同じような書き方ができており、オブジェクト指向ではなくてもJuliaならコンパクトに書けていることがわかります。ですので、
>　 上記の処理をプロセス指向で実装すると、以下の2通りの方法があります。1つはそのまま引数として渡す方法です。

というのはJuliaでは当てはまりません。

## 2-3. オブジェクトの動的操作
「2-3. オブジェクトの動的操作」についてです。まず、元の記事のPythonコードは
```python
class Individual:
    def __init__(self, energy=10):
        self.energy = energy

    def eat_fruit(self):
        self.energy += 1
        return self

    def eat_meat(self):
        self.energy += 2
        return self

    def run(self):
        self.energy -= 3
        return self


anyone = Individual()
print("energy: {}".format(anyone.energy))
anyone.eat_meat()
print("energy after eat_meat: {}".format(anyone.energy))
anyone.eat_fruit()
print("energy after eat_fruit: {}".format(anyone.energy))
anyone.run()
print("energy after run: {}".format(anyone.energy))
anyone.eat_meat().run()
print("energy after eat_meat and run: {}".format(anyone.energy))

```
です。```anyone.eat_meat().run()```のように繋がっているのが便利ですね。Juliaでこれを書くと、
```julia
mutable struct Individual
    energy
    Individual(energy=10) = new(energy)
end
function eat_fruit(self::Individual) 
    self.energy += 1
    return self
end
function eat_meat(self::Individual) 
    self.energy += 2
    return self
end
function run(self::Individual) 
    self.energy -= 3
    return self
end

anyone = Individual()
println("energy: $(anyone.energy)")
eat_meat(anyone)
println("energy after eat_meat: $(anyone.energy)")
eat_fruit(anyone)
println("energy after eat_fruit: $(anyone.energy)")
run(anyone)
println("energy after run: $(anyone.energy)")
eat_meat(anyone) |> run
println("energy after eat_meat and run: $(anyone.energy)")
```
となります。出力は
```
energy: 10
energy after eat_meat: 12
energy after eat_fruit: 13
energy after run: 10
energy after eat_meat and run: 9
```
です。Juliaではパイプライン演算子```|>```を使うことで、Pythonと同様に連結することができます。

次に、元の記事のPythonコードは
```python
class Boy(Individual):
    def daily_activity(self):
        self.eat_meat().eat_meat().run().eat_meat().eat_fruit().run().eat_meat()
        print("boy's daily energy: {}".format(self.energy))


class Girl(Individual):
    def daily_activity(self):
        self.eat_meat().eat_fruit()
        print("girl's daily energy: {}".format(self.energy))


bob = Boy()
bob.daily_activity()
mary = Girl()
mary.daily_activity()

```
と```Individual```の性質を持った```Boy```と```Girl```を定義しています。いちいち```Boy```用に```eat_meat```などを定義しなくてもよい、というところが便利なところかと思います。
これをJuliaで実現する場合は、例えば、
```julia
abstract type Individual end

function eat_fruit(self::Individual) 
    self.energy += 1
    return self
end
function eat_meat(self::Individual) 
    self.energy += 2
    return self
end
function run(self::Individual) 
    self.energy -= 3
    return self
end

mutable struct Boy <: Individual
    energy
    Boy(energy=10) = new(energy)
end

function daily_activity!(self::Boy)
    eat_meat(self) |> eat_meat |> run |> eat_meat |> eat_fruit |> run |> eat_meat
    println("boy's daily energy: $(self.energy)")
end

mutable struct Girl <: Individual
    energy
    Girl(energy=10) = new(energy)
end
function daily_activity!(self::Girl)
    eat_meat(self) |> eat_fruit
    println("girl's daily energy: $(self.energy)")
end

bob = Boy()
daily_activity!(bob)
mary = Girl()
daily_activity!(mary)

```
となります。先程の```Individual```を抽象型に変更しました。これは```Individual```を自分でいじれるようなコードであればよいですが、そうでない場合、```Individual```を変更しないPythonのような形で書けるでしょうか。一つの方法は、```Individual```型をフィールドに持たせ、
```julia
mutable struct Boy 
    indivisual::Individual
end
```
のようにすることです。これだと、```Individual```で定義した関数を使いたい場合には毎回```A.indivisual```のようにしなければならないですが、これは楽に定義する方法があります。[こちらの記事](https://qiita.com/hidemotoNakada/items/65b9190362f0796c1b5a)を参考にしますと、```MacroTools```というパッケージを使うと楽そうです。これは
```julia
import MacroTools
@MacroTools.forward Boy.individual eat_fruit
@MacroTools.forward Boy.individual eat_meat
@MacroTools.forward Boy.individual run
@MacroTools.forward Boy.individual get_energy
```
とします。全体としては、
```julia
mutable struct Individual
    energy
    Individual(energy=10) = new(energy)
end
function eat_fruit(self::Individual) 
    self.energy += 1
    return self
end
function eat_meat(self::Individual) 
    self.energy += 2
    return self
end
function run(self::Individual) 
    self.energy -= 3
    return self
end
function get_energy(self::Individual)
    return self.energy
end

mutable struct Boy 
    individual::Individual
end

import MacroTools
@MacroTools.forward Boy.individual eat_fruit
@MacroTools.forward Boy.individual eat_meat
@MacroTools.forward Boy.individual run
@MacroTools.forward Boy.individual get_energy


function Boy(energy=10)
    individual = Individual(energy)
    return Boy(individual)
end

function daily_activity!(self::Boy)
    eat_meat(self) |> eat_meat |> run |> eat_meat |> eat_fruit |> run |> eat_meat
    println("boy's daily energy: $(get_energy(self))")
end


mutable struct Girl 
    individual::Individual
end
function Girl(energy=10)
    individual = Individual(energy)
    return Girl(individual)
end
@MacroTools.forward Girl.individual eat_fruit
@MacroTools.forward Girl.individual eat_meat
@MacroTools.forward Girl.individual run
@MacroTools.forward Girl.individual get_energy

function daily_activity!(self::Girl)
    eat_meat(self) |> eat_fruit
    println("girl's daily energy: $(get_energy(self))")
end

bob = Boy()
daily_activity!(bob)
mary = Girl()
daily_activity!(mary)
```
となります。ここで、```energy```を手に入れる関数として```get_enery```を新しく定義しておきました。
どちらにせよ、Pythonのオブジェクト指向によく似た書き方になっているかと思います。ですので、元記事にあるような
> 　また、主語, 動詞, 目的語の構造は比較的に理解しやすいです。上記の例では、まずeat_meat()、次にrun()という一連の動作が永遠に続いても理解できます。プロセス指向で実現するとboy_energy = eat_meat(boy_energy); boy_energy = run(boy_energy);...のような長文になるか、eat_meat(run(boy_energy))のような階層構造になるので、理解しにくくなるでしょう。

プロセス指向の問題はJuliaでは解決されていることがわかります。



# まとめ
以上のように、オブジェクト指向でやれることは、Juliaにおいては、多重ディスパッチによってほぼ同じように実現されていることがわかります。