今日のゴール
- Nim言語の開発環境を構築する
- Nindaライブラリをインストールする
- 最初の「Hello, Tuple Space!」プログラムを実行する
Nim言語とは
NindaはNim言語で書かれています。まずはNimについて簡単に紹介しましょう。
Nim(旧称Nimrod)は、2008年にAndreas Rumpfによって開発されたプログラミング言語です。その特徴は:
- Pythonライクな読みやすい構文: インデントベース、直感的
- Cレベルの実行速度: CやC++にコンパイルされる
- 強力なメタプログラミング: マクロ、テンプレート
- ガベージコレクション: メモリ管理が自動(ARC/ORC)
- クロスプラットフォーム: Windows、macOS、Linux対応
# Nimのサンプルコード
proc greet(name: string): string =
result = "Hello, " & name & "!"
echo greet("World") # Hello, World!
Step 1: Nimのインストール
macOS
Homebrewを使うのが最も簡単です:
# Homebrewがない場合は先にインストール
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Nimのインストール
brew install nim
# バージョン確認
nim --version
# Nim Compiler Version 2.0.x
Linux (Ubuntu/Debian)
choosenimを使ったインストールが推奨されます:
# choosenimのインストール
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
# パスを通す(.bashrcまたは.zshrcに追加)
export PATH=$HOME/.nimble/bin:$PATH
# 最新の安定版をインストール
choosenim stable
# バージョン確認
nim --version
Windows
- 公式サイトからインストーラをダウンロード
- インストーラを実行
- 環境変数PATHに
C:\Nim\binを追加 - コマンドプロンプトで確認:
nim --version
バージョン確認
Nindaを使用するには、Nim 2.0以上が必要です:
nim --version
# Nim Compiler Version 2.0.0 [macOS: amd64]
# Compiled at 2023-xx-xx
Step 2: Nindaのインストール
方法1: ソースからインストール
# リポジトリをクローン
git clone https://github.com/jasagiri/ninda.git
cd ninda
# ディレクトリ構造の確認
ls -la
# src/ - ソースコード
# tests/ - テスト
# docs/ - ドキュメント
方法2: Nimbleパッケージとして(将来的に)
# 将来的にはこのコマンドでインストール可能になる予定
nimble install ninda
Step 3: プロジェクト構造
Nindaのソースコード構造を確認しましょう:
Step 4: 最初のプログラム
それでは、最初のNindaプログラムを書いてみましょう!
hello_tuple.nim
# hello_tuple.nim
import std/asyncdispatch
# nindaディレクトリの親からインポートするパス設定
import ../src/ninda
proc main() {.async.} =
echo "=== Hello, Tuple Space! ==="
echo ""
# 1. SpaceManagerを作成
let manager = newSpaceManager()
echo "SpaceManager created."
# 2. デフォルトのTupleSpaceを取得
let ts = manager.space()
echo "TupleSpace acquired."
echo ""
# 3. タプルを書き込む
echo "Writing tuple: (\"greeting\", \"Hello, World!\")"
await ts.writeAsync(toTuple(strVal("greeting"), strVal("Hello, World!")))
echo "Tuple written."
echo ""
# 4. タプルを読み取る(非破壊的)
echo "Reading tuple with pattern: (\"greeting\", ?)"
let readResult = await ts.tryReadAsync(toPattern(strVal("greeting"), nilValue()))
if readResult.isSome:
let tuple = readResult.get()
echo "Found: ", tuple
echo "Message: ", tuple[1].strVal
else:
echo "No tuple found!"
echo ""
# 5. タプルを取り出す(破壊的)
echo "Taking tuple with pattern: (\"greeting\", ?)"
let takeResult = await ts.tryTakeAsync(toPattern(strVal("greeting"), nilValue()))
if takeResult.isSome:
echo "Taken: ", takeResult.get()
else:
echo "No tuple to take!"
echo ""
# 6. もう一度読もうとする(空になっているはず)
echo "Trying to read again..."
let emptyResult = await ts.tryReadAsync(toPattern(strVal("greeting"), nilValue()))
if emptyResult.isSome:
echo "Found: ", emptyResult.get()
else:
echo "Space is empty now! (as expected)"
echo ""
echo "=== Done! ==="
waitFor main()
コンパイルと実行
# nindaディレクトリにいることを確認
cd ninda
# コンパイル
nim c -r examples/hello_tuple.nim
期待される出力:
=== Hello, Tuple Space! ===
SpaceManager created.
TupleSpace acquired.
Writing tuple: ("greeting", "Hello, World!")
Tuple written.
Reading tuple with pattern: ("greeting", ?)
Found: ("greeting", "Hello, World!")
Message: Hello, World!
Taking tuple with pattern: ("greeting", ?)
Taken: ("greeting", "Hello, World!")
Trying to read again...
Space is empty now! (as expected)
=== Done! ===
コードの解説
1. インポート
import std/asyncdispatch
import ../src/ninda
-
std/asyncdispatch: Nimの非同期処理ライブラリ -
../src/ninda: Nindaのメインモジュール
2. SpaceManagerの作成
let manager = newSpaceManager()
SpaceManagerは、複数のタプルスペースを管理するオブジェクトです。設定オプションを渡すこともできます:
# カスタム設定
let config = SpaceManagerConfig(
channelSize: 10000, # メッセージキューサイズ
walPath: "" # WALパス(永続化なし)
)
let manager = newSpaceManager(config)
3. TupleSpaceの取得
let ts = manager.space()
引数なしで呼ぶとデフォルトスペースを取得します。名前付きスペースも作れます:
let orders = manager.space("orders")
let inventory = manager.space("inventory")
4. タプルの書き込み
await ts.writeAsync(toTuple(strVal("greeting"), strVal("Hello, World!")))
-
toTuple(): TupleValueのシーケンスを作成 -
strVal(): 文字列のTupleValueを作成 -
writeAsync(): 非同期でタプルを書き込み
5. パターンマッチングで読み取り
let pattern = toPattern(strVal("greeting"), nilValue())
let result = await ts.tryReadAsync(pattern)
-
toPattern(): パターンを作成 -
nilValue(): ワイルドカード(何でもマッチ) -
tryReadAsync(): 非ブロッキング読み取り
よくあるエラーと対処法
1. コンパイルエラー: モジュールが見つからない
Error: cannot open file: ninda
対処: インポートパスを確認。相対パスで正しく指定されているか確認してください。
2. Nimのバージョンが古い
Error: undeclared identifier: 'some feature'
対処: choosenim stable でNimをアップデートしてください。
3. async関連のエラー
Error: 'async' requires the 'asyncdispatch' module
対処: import std/asyncdispatch を追加してください。
開発環境のおすすめ設定
VSCode
- 拡張機能「Nim」をインストール
- 設定でnimのパスを指定
{
"nim.nimsuggestPath": "/path/to/nimsuggest"
}
Vim/Neovim
" nim.vimプラグイン
Plug 'zah/nim.vim'
Emacs
(use-package nim-mode
:ensure t)
練習問題
問題3-1: 数値タプル
整数値を含むタプルを書き込み、読み取るプログラムを書いてください。
ヒント
-
intVal(42)で整数値を作成 -
toTuple(strVal("number"), intVal(42))でタプルを構成 -
tryReadAsyncで読み取り、result[1].intValで値を取得
問題3-2: 複数のタプル
3つの異なるタプルを書き込み、すべて読み取るプログラムを書いてください。
ヒント
- 3回
writeAsyncを呼び出す -
readAllAsyncで全件取得するか、個別にtryReadAsync - 各タプルは異なるパターンでマッチするよう設計
問題3-3: 複合タプル
文字列、整数、浮動小数点を含む3要素タプルを扱うプログラムを書いてください。
ヒント
toTuple(strVal("data"), intVal(100), floatVal(3.14))- 読み取り時は
toPattern(strVal("data"), nilValue(), nilValue()) - 各要素は
result[0].strVal,result[1].intVal,result[2].floatValで取得
まとめ
今日は開発環境を構築し、最初のNindaプログラムを動かしました:
- Nimのインストール: choosenimまたはパッケージマネージャを使用
- Nindaのセットアップ: ソースからクローン
-
基本的なプログラム構造:
- SpaceManagerの作成
- TupleSpaceの取得
- write/read/takeの基本操作
次回予告
明日はDay 4「基本操作 〜write, read, takeを完全理解〜」です。
今日触れた3つの基本操作を、より詳しく、様々なユースケースとともに学んでいきます。ブロッキング操作と非ブロッキング操作の違い、操作の原子性についても解説します。
前回: Lindaモデルとは | 目次 | 次回: 基本操作