Nimでオブジェクト指向する

先日Nimでオブジェクト指向っぽいコードを書いたのですが、まとまってる記事などが見当たらなかったので軽いまとめです。
内容の高度さで言えば

class Greeting {
    fun printHello(s: String) {
        println("Hello, $s")
    }
}

fun main() {
    val greeting = Greeting()
    greeting.printHello("World")
    // > Hello, World
}

くらいのことしか言わないので知ってるよって人は読まないで大丈夫です。

※編集後追記:公式以上のことは書いてないのでもう公式を読めばいいんじゃないかな*1

クラス(っぽいもの)を作る

Nimにはクラスの概念がないのですが、構造体を利用してクラスっぽいものを作ることができます。

日本語だと下2つの記事がまとまってると感じたので是非見てみてください。

公式がよければ公式ではNim Tutorial (Part Ⅱ)にて説明されています。

公式では説明されているのですが、記事でやたら継承されているRootObjとはなんぞやということですが、Nimでは何も継承していないobjectはfinalとして扱われるので、そのobjectを継承した別のobjectを作りたい場合はRootObjを継承しようね、というものであり、それ以上のことは何もないようです。

メソッド(っぽいもの)を作る

「え、お前クラスの説明してないじゃん」と言われたらごめんなさいですけど、クラスについては公式、または英語がキツいと感じても上で示した2つの記事を読めばだいたい大丈夫だと思うので、ここからはメソッドについて書きたいと思います。

むしろクラスの作り方についてはまとまってるものがあったのですが、メソッドについてまとめられたものが見当たらなかったので書きたいことはここからです。

上で示したQiitaや公式にもある通り、Nimではクラスはなく、あくまで構造体をクラスっぽく扱えるのでクラスのメンバ関数はありません。*2
その代わり、関数proc sum(a: int, b: int) : int = a + bsum(1, 2)または1.sum(2)のように書くことができます。
ちょっと例が微妙ですが説明すると、関数を呼び出す際に、その関数を第一引数の型のメソッドのように扱えるので、これを利用してメソッドっぽいものが作れます。

Nim公式によると、クラスにメソッドを追加できなくすることによって

  • メソッドがどのクラスに所属しているかいちいち気にする必要がなくなる
  • 上の1.sum(2)のように既存のクラスや外部のクラスにもメソッドを追加したっぽくできるようになる

の2点のメリットがあるとのことです。2点目はKotlinの拡張関数みたいで確かになかなか素敵に使えました。

関数の宣言

Nimで関数の宣言に使うキーワードにはprocmethodfuncの三種類があります。

まず、methodは引数にobjectを取らなければならない制約があるので、これは言葉の通りメソッドとして使うことが前提とされているような気がします。
staticメソッドが欲しかったらprocで宣言する必要がありそうです。

次に、procは引数の型がコンパイル時に決定されるのに対し、methodキーワードを使うとそれらが動的に決定されます。
ものすごくざっくりと言うとポリモフィズムを適用したかったらmethodを使おうね、ということだと僕は解釈しました。

また、どうやらmethodではprocで効く最適化が一部効かなくなるようです。

funcは大体の部分がprocと同じですが、副作用のない関数であることを保証するキーワードになっています。
Nimのややマイナーな言語仕様(funcとnot nil) - Qiita

まとめ

ここまでの内容で、冒頭で出したようなやり方でHello, Worldを出すことができるかと思います。

type Greeting = ref object

# base methodにしないとWarningが出て気持ち悪かったので{.base.}
method printHello(greeting: Greeting, s: string) {.base.} = 
    echo "Hell, " & s 

# Nimではmainメソッドはないのでトップレベルに書いた処理が実行される
when isMainModule:
    let greeting = Greeting()
    greeting.printHello("World")  

できた。

あとはお好みでpragmaを。下の記事が詳しいです。これはあまり公式に書いてなくてつらい、、*3

nimのpragmaについて - Qiita


ちなみに僕がNimでオブジェクト指向してみたのはなんとなく作ったライフゲームですので、参考になるかはわかりませんがもしよければ。

*1:まとめなのでそれはそう。やっぱ日本語だよね、手っ取り早くNimでOOPしたい、という方の役に立てればと思います。

*2:「メソッド」と表記が揺れましたが、「クラスの保有する」の意を強く表したかったのでここでは「メンバ関数」としました。それ以上の意味はないです。

*3:見落としの可能性大なので知ってたら教えてください。