概要
Nim言語の勉強がてら、JSONの深さを指定して整形表示するツールを作ってみました。
この記事では、Nim言語の布教も兼ねて、
整形表示ツールの制作過程で見つかったNim言語の良さを共有させていただきたいと思います。
完成したツールのデモンストレーション
まず、いきなりですが、完成したコマンドのデモを以下に示したいと思います。
$ ./jsonfmt --depth 0 '{"num1":4,"num2":3,"data1":{"length":26,"text":"AAAAA","data2":{"num3":1}}}'
{"num1":4,"num2":3,"data1":{"length":26,"text":"AAAAA","data2":{"num3":1}}}
$ ./jsonfmt --depth 1 '{"num1":4,"num2":3,"data1":{"length":26,"text":"AAAAA","data2":{"num3":1}}}'
{
"num1": 4,
"num2": 3,
"data1": {"length":26,"text":"AAAAA","data2":{"num3":1}}
}
$ ./jsonfmt --depth 2 '{"num1":4,"num2":3,"data1":{"length":26,"text":"AAAAA","data2":{"num3":1}}}'
{
"num1": 4,
"num2": 3,
"data1": {
"length": 26,
"text": "AAAAA",
"data2": {"num3":1}
}
}
$ ./jsonfmt --depth 3 '{"num1":4,"num2":3,"data1":{"length":26,"text":"AAAAA","data2":{"num3":1}}}'
{
"num1": 4,
"num2": 3,
"data1": {
"length": 26,
"text": "AAAAA",
"data2": {
"num3": 1
}
}
}
JSONを木構造として見立てた時に、フォーマットする最大の木の深さを--depth
オプションで指定できるようにしています。
--depth 0
を指定するとただ単にJSONを圧縮する処理になり、--depth
を1以上にすると木のdepth
以下の階層で整形するようにしています。
Github: https://github.com/cacapouh/jsonfmt
こちらのコマンドはHomebrewでインストールできるようにしていますので、
もしよかったら使ってみてください m(_ _)m
$ brew tap cacapouh/jsonfmt
$ brew install cacapouh/jsonfmt/jsonfmt
Nimの良さ
cligenが最強
いきなり、Nim言語じゃなくて、Nim言語のライブラリの話なって恐縮ですが、cligenというライブラリについて共有させていただきます。
こちらのライブラリは、コマンドライン引数の受け取りの実装で使用しました。
使い方は簡単で cligenで提供されているdispatch
関数の引数に自作の関数を渡してやるだけで、CLIツールとして使えるようになります。
例えば、以下のような実装をした場合、
import std/strutils
proc nimEcho(repeat: int = 1, args: seq[string]): int =
echo args[0].repeat(repeat).join()
0 # exit code
when isMainModule:
import cligen
dispatch(nimEcho)
以下のようにCLIツールとして呼び出すことができます。
$ ./example "hoge"
hoge
$ ./example --repeat 10 "hoge"
hogehogehogehogehogehogehogehogehogehoge
cligen
というライブラリはかなり強力で、
タイポした際に指摘してくれたり、自動で--help
オプションをサポートしてくれたりします。
$ ./example --repleat 10 "hoge"
Unknown long option: "repleat"
Maybe you meant one of:
repeat
Run with --help for full usage.
$ ./example --help
Usage:
nimEcho [optional-params] [args: string...]
Options:
-h, --help print this cligen-erated help
--help-syntax advanced: prepend,plurals,..
-r=, --repeat= int 1 set repeat
最強ですね 🤩
メソッドチェーンの仕組みが優秀
Nimでは以下のように、ドット演算子で関数呼び出しをすることができます。
proc addOne(num: int): int =
num + 1
echo 10.addOne # 11
echo addOne(10) # 11(普通に呼び出した場合)
標準ライブラリの呼び出しにも、このドット演算子を利用することができまして、
簡単にメソッドチェーンを構築することができます。
具体例を挙げますと、JSON整形ツールでは、以下のような実装でJSONの各キーバリューを整形しています。
雰囲気だけ伝われば幸いですが、メソッドチェーンを駆使した実装になっていまして、
(ワンライナーで複雑な処理を書くのは、考えようですが)かなり楽に実装することができました。
let kvs = jsonFileds.toSeq.mapIt(convert(it)).mapIt(&"{nextState.indent()}{it}").join(",\n")
&"{{\n{kvs}\n}}"
軽量プログラミング言語 & 静的型付け言語
Pythonと比較した話になりますが、
型が静的で、コンパイルするだけで軽いテストになります故かなり開発しやすかったです。
軽量プログラミング言語(LL)のような側面を持ちながら、静的型付け言語の恩恵を受けられるというが嬉しいですね。
Nimはジェネリクスも利用可能でして、型を駆使した実装もやれそうです。
proc identity[T](value: T): T =
value
echo identity "あああ" # 「あああ」と標準出力される
オブジェクトの生成も簡単
Nim言語はオブジェクト指向もサポートしていまして、簡単にオブジェクトを生成することができます。
以下はオブジェクトを使ったサンプルコードですが、やはり新しめの言語ということもあって、この辺の文法もスッキリしていますね
import std/strformat
type Language = object
name: string
version: string
proc show(language: Language): string = &"{language.name} {language.version}"
let scala: Language = Language(name: "Scala", version: "3.0.2")
echo show(scala) # Scala 3.0.2
継承を使用する場合は以下のように of
キーワードで継承元を指定できます。
type
Animal = object of RootObj
age: int
Cat = object of Animal # Animalというオブジェクトが継承元
owerName: string
ビルド/実行がシンプル
最後にちょっと微妙な内容かもしれないですが、ビルドして実行可能なバイナリファイルができる上がるというのが、
シンプルでいいですね。
$ nim c jsonfmt.nim
Hint: used config file '/Users/tobita_yoshiki/Nim/config/nim.cfg' [Conf]
Hint: used config file '/Users/tobita_yoshiki/Nim/config/config.nims' [Conf]
......................................................................................................................
CC: ../../Nim/lib/system.nim
CC: ../../Nim/lib/pure/collections/tables.nim
CC: ../../Nim/lib/pure/strutils.nim
CC: ../../Nim/lib/pure/strformat.nim
CC: ../../Nim/lib/pure/os.nim
CC: ../../.nimble/pkgs/cligen-1.5.31/cligen/parseopt3.nim
CC: ../../.nimble/pkgs/cligen-1.5.31/cligen/textUt.nim
CC: ../../.nimble/pkgs/cligen-1.5.31/cligen/argcvt.nim
CC: ../../.nimble/pkgs/cligen-1.5.31/cligen/sysUt.nim
CC: jsonfmt.nim
Hint: [Link]
Hint: gc: refc; opt: none (DEBUG BUILD, `-d:release` generates faster code)
84615 lines; 0.864s; 116.832MiB peakmem; proj: /Users/tobita_yoshiki/work/jsonfmt/jsonfmt.nim; out: /Users/tobita_yoshiki/work/jsonfmt/jsonfmt [SuccessX]
$ ls
README.md build jsonfmt jsonfmt.nim