JuliaでPrivate属性をつけてみたい：見た目をPythonのオブジェクト指向のようにする in Julia


#PythonっぽいJulia

突然ですが、以下のJuliaで書かれたコードを見てください。

```julia
using .Personmodule
human1 = Person("Taro",22,"大学生")
valname = :human1
println("$(valname)の名前は ",human1.get_name())
println("$(valname)の年齢は ",human1.get_age())
println("$(valname)の職業は ",human1.get_job())

human1.set_age(23)
println("$(valname)の年齢は ",human1.get_age())
human1.set_job("大学院生")
println("$(valname)の職業は ",human1.get_job())


human2 = Person("Hanako",28)
valname = :human2
println("$(valname)の名前は ",human2.get_name())
println("$(valname)の年齢は ",human2.get_age())
println("$(valname)の職業は ",human2.get_job())

println("$(valname)の職業は ",human1.job)
```
Juliaなのに、Pythonっぽく見えませんか？

なお、これを実行すると、

```
human1の名前は Taro
human1の年齢は 22
human1の職業は 大学生
human1の年齢は 23
human1の職業は 大学院生
human2の名前は Hanako
human2の年齢は 28
human2の職業は 無職
ERROR: LoadError: type Person has no field job
```
となります。```get_job```で変数を取ってこられるにも関わらず、Personという型には```job```という変数がないようです。
つまり、**jobという変数が隠蔽されている**わけです。
Juliaでは通常使えない**private属性**が使えているように見えますね。

このコードで使われているPersonmoduleとPerson型についての解説をしたいと思います。
なお、この書き方による計算速度の変化などは全く調べていませんので、この書き方が良いかどうかはわかりません。
目的は、「見た目をPythonっぽくする」ことだけです。


使った機能は

1. クロージャ
2. Typeのfieldにfunctionを定義
3. 無名関数

です。

# よくある定義の仕方
mutable structをよくある定義の仕方をすると、

```julia
    mutable struct Person
        name::String
        age::Int64
        job::String
   end
```
となりますが、この場合、ageやjobは外から呼べてしまいます。

```julia
a = Person("Taro",23,"無職")
println(a.age)
```

# Privateな変数がある定義
## フィールドから削除
このようにageにアクセスできるのが嫌な場合、オブジェクト指向な言語ではprivate属性をつけることができて、その場合は
外からは決してアクセスできなくなります。その場合には、何か関数を作ってその関数を経由して値を操作します。
ageにアクセスされたくないのであれば、

```julia
    mutable struct Person
        name::String
   end
```
とageをstructのフィールドから削除してしまいましょう。
これで、```a.age```では呼べなくなります。
一方、これではageの値をどこにも保持していないじゃないか、と思うと思います。そこで、クロージャーを使います。

## クロージャー
参考URL
「Julia で Closure のパフォーマンスを気にしてみる」
https://qiita.com/antimon2/items/8d464d54cf2da6239635
これを使えば、関数の中で値を保持することができます。
よって、

```julia
module Personmodule
    export Person
    mutable struct Person
        name::String
        get_name::Function
        set_name::Function
        get_age::Function
        set_age::Function
        get_job::Function
        set_job::Function
        
        function Person(name,age,job)
            #Private属性の変数たち
            #ここから
            _age::Int64 = age
            _job::String = job
            #ここまで

            self = new()
            self.name = name
            self.get_name = () -> self.name
            self.set_name = (name::String) -> (self.name = name;return nothing)

            self.get_age = () -> _age
            self.get_job = () -> _job

            set_age(age::Int64) = (_age = age ;return nothing)
            set_job(job::String) = (_job = job ;return nothing)

            self.set_age = (age) -> set_age(age)
            self.set_job = (job) -> set_job(job)

            return self
        end

        function Person(name,age)
            return Person(name,age,"無職")
        end

    end
end
```
としました。関数```Person```は```mutable struct Person```の「内部」にあることに注意してください。
これにより変数```_age```と```_job```はその値が保持され、かつ外からは見えません。変数のアンダースコアはPythonなどでprivate変数をつけるときに使うようなのでつけてみました。

## Typeのfieldにfunctionを定義
次に、外から何らかの関数を通じて```_age```にアクセスできないといけませんので、関数をPerson typeのフィールドに用意します。
そして、その関数を関数```Person```の中で定義します。このとき、```_age```の値を手に入れたり変更したりできるようにしています。

## 無名関数

ここで、```get_age::Function```が関数であることに注意してください。関数を定義するために、```self.get_age = () -> _age```となっています。
ここで、```() -> _age```は無名関数と呼ばれるもので、この場合には、引数0個で呼ばれたら```_age```を返す関数、というものです。
```self.set_age = (age) -> set_age(age)```も同様で、引数ageとともに呼ばれたら、``` set_age(age)```を返す関数、を定義しています。
```set_age(age::Int64) = (_age = age ;return nothing)```は関数```set_age(age::Int64)```を定義しているところです。変数```_age```をセットする関数になっており、返り値は必要ないので```return nothing```になっています。

#全体のコード
全体のコードは以下の通りです。

```julia
module Personmodule
    export Person
    mutable struct Person
        name::String
        get_name::Function
        set_name::Function
        get_age::Function
        set_age::Function
        get_job::Function
        set_job::Function
        
        function Person(name,age,job)
            #Private属性の変数たち
            #ここから
            _age::Int64 = age
            _job::String = job
            #ここまで

            self = new()
            self.name = name
            self.get_name = () -> self.name
            self.set_name = (name::String) -> (self.name = name;return nothing)

            self.get_age = () -> _age
            self.get_job = () -> _job

            set_age(age::Int64) = (_age = age ;return nothing)
            set_job(job::String) = (_job = job ;return nothing)

            self.set_age = (age) -> set_age(age)
            self.set_job = (job) -> set_job(job)

            return self
        end

        function Person(name,age)
            return Person(name,age,"無職")
        end

    end
end

using .Personmodule
human1 = Person("Taro",22,"大学生")
valname = :human1
println("$(valname)の名前は ",human1.get_name())
println("$(valname)の年齢は ",human1.get_age())
println("$(valname)の職業は ",human1.get_job())

human1.set_age(23)
println("$(valname)の年齢は ",human1.get_age())
human1.set_job("大学院生")
println("$(valname)の職業は ",human1.get_job())


human2 = Person("Hanako",28)
valname = :human2
println("$(valname)の名前は ",human2.get_name())
println("$(valname)の年齢は ",human2.get_age())
println("$(valname)の職業は ",human2.get_job())

println("$(valname)の職業は ",human1.job)


```

なお、このやり方がわかったきっかけは以下の中国語のページです（中国語は全く読めません）。
「如何在 struct 中定义 private field?」
https://discourse.juliacn.com/t/topic/560/5