はじめに
Nimはサポートは最小限だけどOOP(オブジェクト指向プログラミング)ができる。
NimでOOPのやり方
以下に良記事があるので参考されたしー。
マクロを使ってclass定義
上のNim by Exampleにマクロを使っていい感じにclass定義できるマクロが載ってる
Nim by Example - Macros
こんな感じに定義できる。
クラス定義コード引用
class Animal of RootObj:
var name: string
var age: int
method vocalize: string = "..."
method age_human_yrs: int = self.age # `self` is injected
class Dog of Animal:
method vocalize: string = "woof"
method age_human_yrs: int = self.age * 7
class Cat of Animal:
method vocalize: string = "meow"
イケてるけど
pythonみたいにself使えたり
new+クラス名でコンストラクタを定義できたり
かなりイケてるんだけどちょっと不満点が。
継承元を定義する必要がある。
継承元を定義しなかったらRootObjを継承させればいんじゃね?
exportが不自然
できるけどこんな感じクラス名とアスタリスクの間にスペースがはいるのでちょっと不自然。
class Animal * of RootObj:
var name: string
もうデフォでexportすればいいんじゃね(過激派)
オブジェクト以外も定義したい
タプルとかも同じように書いて関数を定義できたらいいなと。
こんな感じでさ。
class Size of tuple[width, height: int]:
proc area(): int =
self.width * self.height
methodとprocしか定義できない
IteratorとかTemplateも定義したいね。
作った
個人的に使う分には問題ない。
マクロ
class.nim
import macros
proc typeName(head: NimNode): NimNode =
if head.len == 0:head else: head[1]
proc baseName(head: NimNode): NimNode =
if head.len == 0: newIdentNode("RootObj") else: head[2]
proc isObjectDef(head: NimNode): bool =
head.len == 0 or head[2].kind == nnkIdent
proc buildObjectTypeDecl(head: NimNode): NimNode =
template typeDecl(a, b): untyped =
type a* = ref object of b
getAst(typeDecl(head.typeName, head.baseName))
proc buildBasicTypeDecl(head: NimNode): NimNode =
newNimNode(nnkTypeSection)
.add(newNimNode(nnkTypeDef)
.add(newIdentNode($head[1]))
.add(newNimNode(nnkEmpty))
.add(head[2]))
proc buildTypeDecl(head: NimNode): NimNode =
if head.isObjectDef:
head.buildObjectTypeDecl
else:
head.buildBasicTypeDecl
macro class*(head, body: untyped): untyped =
let
typeName = head.typeName
ctorName = newIdentNode("new" & $typeName)
isObjectDef = head.isObjectDef
var recList = if isObjectDef: newNimNode(nnkRecList) else: nil
result = newStmtList()
result.add(head.buildTypeDecl)
for node in body.children:
case node.kind:
of nnkMethodDef, nnkProcDef, nnkIteratorDef, nnkTemplateDef:
if node.name.kind != nnkAccQuoted and node.name == ctorName:
node.params[0] = typeName
else:
node.params.insert(1, newIdentDefs(ident("self"), typeName))
result.add(node)
of nnkVarSection:
if not isObjectDef:
error "Invalid node: " & node.lispRepr
for n in node.children:
recList.add(n)
else:
result.add(node)
if isObjectDef:
result[0][0][2][0][2] = recList
when isMainModule:
class Animal:
var name: string
var age: int
method vocalize: string {.base.} =
"..."
method age_human_yrs: int {.base.} =
self.age
class Dog of Animal:
method vocalize: string =
"woof"
method age_human_yrs: int =
self.age * 7
class Cat of Animal:
method vocalize: string =
"meow"
class Rabbit of Animal:
proc newRabbit(name: string, age: int) =
result = Rabbit(name: name, age: age)
method vocalize: string =
"meep"
proc `$`: string =
"rabbit:" & self.name & ":" & $self.age
let rabbit = newRabbit("Fluffy", 3)
var animals: seq[Animal] = @[
Dog(name: "Sparky", age: 10),
Cat(name: "Mitten", age: 10),
rabbit
]
for a in animals:
echo a.vocalize()
echo a.age_human_yrs()
echo rabbit
サンプル
sample.nim
import class
import strformat
class Person:
var name: string
proc newPerson(name: string): Person =
Person(name: name)
method greed() {.base.} =
echo fmt"hello my name is {self.name}"
class Size of tuple[width, height: int]:
proc area(): int =
self.width * self.height
class FizzBuzz:
var max: int
iterator items(): string =
for n in 1..self.max:
var str: string
if n mod 3 == 0: str = "fizz"
if n mod 5 == 0: str &= "buzz"
if str.len == 0: str = $n
yield str
class TemplateSample:
var name: string
template nameWith[T](other: T): string =
self.name & " and " & $other
when isMainModule:
newPerson("Alice").greed
echo (width:2, height:3).area
for s in FizzBuzz(max: 15):
echo s
let ts = TemplateSample(name: "Bob")
echo ts.nameWith(43)
echo ts.nameWith('a')
echo ts.nameWith("string")
実行結果
hello my name is Alice
6
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
Bob and 43
Bob and a
Bob and string
以上〜。