さっき決めたブログ

[Nim]Nimの文法を勉強 06 (モジュール・オブジェクト指向プログラミング)

2020年11月8日 11:11 AM01.03KNimPROGRAMNimNim文法

今回はモジュールとオブジェクト指向的な構文の勉強になります。

モジュールとアクセス制御について

Nimには Java や .Net における namespace 的なものは存在せず、モジュールの分割はファイル単位での分割のみになります。
他のモジュール(ファイル)を読み込むには、import 文を使用します。
モジュール内のプロシージャ、変数、宣言で、import された際に公開したいものについては、* を付けておくことで、import した先で参照もできるようになります。
いわゆる、Javaで言うところの public指定のようなものになりますが、Javaの用語で置き換えると、Nimには public と private しか存在しないことになります。

実行結果
ika = 10

複数のモジュールを読み込み名前が被った場合、先頭にモジュール名+[.] を付けてやることで、どちらのモジュールを使用しているかを判別できます。

モジュール名には別名を付けることもできます。

from を用いれば、モジュールから特定のプロシージャや変数のみをインポートして使用することが出来ます。

逆に、except は、モジュールから特定のプロシージャや変数を除外してインポートすることが出来ます。

include

import と似たような機能に、include というものがあります。
これは指定されたファイルの内容をそのまま取り込む機能になります。
他ファイルの内容をコピペしているようなものなので、import文のようにアクセス制御など細かい指定はできません。

オブジェクトの継承

Nimのオブジェクトは継承することができます。 継承については、オブジェクト型 のところで説明しているので、ここでは割愛します。

相互再帰的な型(Mutually recursive type)

Nimでは、互いに参照し合うような型(相互再帰的な型)を定義する場合、同一の type ブロック内で定義する必要があるようです。

実行結果
(ika: ...)

echo で出力したらどうなってしまうのかちょっと気になったのですが、こう出力されるのですね。

型変換について

Nimにおける型変換は以下のうようにして行います。

実行結果
TAKO(ebi) : (uni: 10, kani: 5)
D:\Projects\nim\nimtest01\test01.nim(15) test01
D:\Projects\nim\nimtest01\test01.nim(9) TAKO
D:\devtools\nim\lib\system\fatal.nim(49) sysFatal
Error: unhandled exception: invalid object conversion [ObjectConversionError]

オブジェクトのバリエーションについて

オブジェクトは同一オブジェクトの中で複数のバリエーションを持つ型のグループを定義する事ができます。

実行結果
(objtype: 2, uni: "雲丹")
D:\Projects\nim\nimtest01\test01.nim(11) test01
D:\devtools\nim\lib\system\fatal.nim(49) sysFatal
Error: unhandled exception: 'ebi' is not accessible using discriminant 'objtype' of type 'IKA' [FieldError]

この例では、objtype にセットされる値によって、使用出来るメンバ変数が切り替わります。
objtype の値とアクセスしようとした変数がアンマッチの場合、ランタイムエラーが発生します。

メゾットの定義について

Nimでのメゾットはプロシージャまたは、(この後説明する)メゾットの糖衣構文による実装となります。

実行結果
マグロ5

ダイナミックディスパッチについて

Nimでポリモフィズムを実装しようとした際、以下のコードでは期待通りの出力は得られません。

実行結果
IKA
IKA

これは、プロシージャは通常コンパイル時に静的に実行先が決められてしまうため、引数の型が親オブジェクトのものである限り、親オブジェクトのメゾットしか呼ばれないことになります。

呼び出し時にインスタンスの型をチェックして適切な実行先を動的に決定するためには、プロシージャの定義に、proc ではなく、method を使用します。

実行結果
EBI1
EBI2

ベースとなるメゾットには、base プラグマを付けないとワーニングとなるので、付けています。

method は、第一引数に判定元となるオブジェクトを指定しなくてはならない制約がありますが、他はproc と同様に使用出来ます。
マルチメゾットを使用すると、引数に複数のオブジェクトが存在する場合、その全てが判定の対象となります。
(マルチメゾットを使用するためには、コンパイルのオプションに --multimethods:on を指定する必要があります)

実行結果
IKA-IKA
EBI1-EBI2

プロパティの定義について

Nimでのプロパティの実装について、getterに関しては通常のプロシージャ構文を用いて実現できます。
setter に関しては少し特殊で、以下のように記載します。

実行結果
(uni: 300)

例外について

Nimでの例外は次のように記載します。

実行結果
OverflowError over- or underflow
finally!!!

例外を発生させるには、raise を使用します。

実行結果
Error: unhandled exception: ERROR!!!!! [Exception]

引数は省略可です。
その場合、前回発生した例外をそのまま渡します。

例外に注釈を付ける

対処すべき例外を明確にしたい場合、{.raises.} プラグマを用いると、記載されていない例外が発生し得る場合にコンパイルエラーとなります。
これによって、プログラムの改変等により新たな例外が発生する状況となった場合にコンパイルエラーとなり、対応漏れを防げます(という役割だと思う)

{.effects.} プラグマを使用することで、その時点で発生しうる例外の一覧をヒントとして出してくれます。

コンパイル結果抜粋
D:\Projects\nim\nimtest01\test01.nim(8, 6) Hint: ref IOError [User]
D:\Projects\nim\nimtest01\test01.nim(9, 6) Hint: ref OSError [User]

ただ何故か上の例で書いた OverflowError とかは検知してくれないんですよね。
明示的な raise 由来のものしかダメということ? 役に立つのか疑問。。。

ジェネリックスについて

Nimでもジェネリックスは普通に使えます。

実行結果
iKani:5 iTako:6 iIka:4
sKani:蛸 sTako:烏賊 sIka:蟹

テンプレートについて

テンプレートはNimの抽象構文ツリー上で動作する置換処理らしい。
何か分かったような、分からないような。
コンパイル時に展開される処理であるが、C言語の define のような単なる文字の置換えではなくて、Nimの構文の流れに沿った定義がされるという解釈でよろしいかな?
使い方自体はそれほど難しくなく、プロシージャの proc 部分を template に置換えるイメージ

実行結果
TAKO10

テンプレートは引数としてブロックも渡すことがですます。 その場合、最後の引数の型を untyped として渡すそうです。

実行結果
NOT KANI!!
EBI
SABA

う〜ん、やってる事は分かるけど、typed とか untyped とか、正直自分の中で理解したとは言い難いですね。 この後 macro とかも調べなくてはいけないし、分かるのか?
と思いつつ、今回はこの程度で。。

参考にしたサイトなど

Nim Tutorial (Part I)(Part Ⅱ)
https://nim-lang.org/docs/tut1.html https://nim-lang.org/docs/tut2.html
Nim Tutorial Part Iを日本語訳してみた(後編)
https://qiita.com/KTakahiro1729/items/3f18811267bf4f8075d5
Nim Tutorial Part Ⅱを日本語訳してみた
https://qiita.com/kawadumax/items/a1b807c477dcbf40efa5
【Nim】個人的逆引きリファレンス
http://flat-leon.hatenablog.com/entry/nim_howto
DeepL翻訳
https://www.deepl.com/ja/translator

以上になります。

投稿者プロフィール

KARASU
うーん いろいろ考え中。。。

コメント

コメント取得中...

関連記事

TOPへ