なんかいろいろ調節して、最終的にこうなりました
書くのめんどくさかったから結構AIに手伝ってもらった、
Hyper-Nova言語 技術仕様書
1. はじめに
Hyper-Nova (.hn) は、「美しく読みやすいコード」と「強力な並列処理」を両立するために設計された、静的型付けプログラミング言語です。Pythonに影響を受けたモダンな構文を持ちつつ、タスク間のグローバルな通信機能や、「nullが存在しない(No-Null)」という安全設計を取り入れています。
Hyper-Nova: 自由への突破口。
バージョン: 0.2.0-draft
著者: karumaru (Systematized by Agent)
更新日: 2025年12月
2. 基本構文
2.1 拡張子
ソースファイルの拡張子は .hn です。
2.2 エンコーディング
UTF-8 を使用します。
2.3 コメント
- 1行コメント:
// - 複数行コメント (ダブルクォート):
""" ... """ - 複数行コメント (シングルクォート):
''' ... '''
2.4 インデント
コードブロックの定義にはインデントを使用します。タブまたは4スペースがサポートされます。
2.5 プログラムのエントリポイント
スクリプト形式の実行モデルを採用しています。ファイルコードは最上位レベルから順次実行されます。必須となる main 関数はありません。
3. データ型
Hyper-Novaは静的型付け言語です。型推論が強力ですが、明示的な型指定も可能です。
3.1 プリミティブ型
-
整数型:
-
int(デフォルト, 通常int32),int8,int16,int32,int64 -
intb(任意精度整数) -
uint8~uint64(符号なし)
-
-
浮動小数点型:
-
float(デフォルト, 通常float32),float8,float32,float64,float128
-
-
真偽値:
boolean(true,false) -
文字列:
string(ダブルクォーテーション) -
文字:
char -
固定小数点:
Decimal -
特殊型:
-
Empty: 値が存在しないことを示します(nullの代わり)。他の型との演算はできません。 -
Err: エラー状態を表します。論理エラー(期待される失敗)を示し、タスクは正常終了します。 -
Crash: システム障害を表します。致命的なエラーを示し、タスクは異常終了し、再起動が可能です。
-
3.2 リテラル表記
-
ビット数・進数指定:
<サイズ>bit<進数>: <値>- 進数は2, 4, 6, 8, 10, 12, 14, 16進数を指定可能
- 例:
8bit16: register_value = 0xFF(8ビット16進数)
-
数値リテラルの区切り文字: 数値リテラルの可読性向上のため、スペースを区切り文字として使用できます。
- 例:
const SPEED = 300 000 000(300000000と同等) - 物理定数やビットマスクなど、巨大な数値の読み間違いを防ぎます。
- 例:
4. 変数と定数
4.1 宣言
-
型推論 (推奨):
name = "Hyper-Nova" // string型として推論 age = 18 // int型として推論 -
明示的型指定:
int8: age = 18
4.2 定数
const で宣言します。即時の初期化が必要です。
const PI = 3.14159265
const SPEED_OF_LIGHT = 299 792 458 // スペース区切りで可読性向上
4.3 型エイリアス
alias キーワードを使用して、既存の型や Err 型に名前をつけることができます。
alias NetworkError = Err("Network")
alias DatabaseError = Err("Database")
alias UserID = int64
型エイリアスのメリット:
- エラーのカテゴリ化が容易になる
-
err名前空間のチェック時に可読性が向上 - 複雑な型定義を簡潔に記述できる
4.4 物理次元型 (Physical Dimension Types)
Hyper-Novaは物理計算の安全性を極限まで高めるため、次元を持つ型システムを導入しています。長さ、時間、質量などの物理量に単位と次元を付与し、不適切な単位の取り扱いを型エラーとして検出できます。
基本的な使用方法
// 質量の宣言
Mass = 0.5 kg
// 長さと時間
distance = 10 km
time = 2 h
// 次元の異なる型同士の加算はエラー
// error = distance + Mass // コンパイルエラー: 次元が違う!
// 除算で新しい次元が生成される
speed = distance / time // 自動的に速度の型になる (単位: m/h)
自動次元チェック
距離 (m) を時間 (s) で割ると、自動的に速度 (m/s) の型になる:
d = 10 km
t = 2 h
result = d / t // 演算結果の単位は m/h
距離 (m) に質量 (kg) を足そうとしたら、「次元が違うよ!」ってコンパイルエラーを出す:
distance = 100 m
mass = 50 kg
// sum = distance + mass // ERROR: 次元が一致しません!
自動単位変換
異なる単位でも次元が同じなら、自動変換して計算できます。
memory_size = 2 MB
cache_size = 1_048_576 bit
// 自動的に単位を揃えて計算
print(memory_size + cache_size -> kB) // 出力: 2131.07 kB
対応単位系
Hyper-Novaは包括的な単位系に対応しています:
SI基本単位
- 長さ: メートル (m)
- 質量: キログラム (kg)
- 時間: 秒 (s)
- 電流: アンペア (A)
- 熱力学温度: ケルビン (K)
- 物質量: モル (mol)
- 光度: カンデラ (cd)
SI組立単位(固有の名称を持つもの)
- 平面角: ラジアン (rad)
- 立体角: ステラジアン (sr)
- 周波数: ヘルツ (Hz)
- 力: ニュートン (N)
- 圧力・応力: パスカル (Pa)
- エネルギー・仕事・熱量: ジュール (J)
- 仕事率・工率・放射束: ワット (W)
- 電荷: クーロン (C)
- 電位差・起電力: ボルト (V)
- 静電容量: ファラド (F)
- 電気抵抗: オーム (Ω)
- コンダクタンス: ジーメンス (S)
- 磁束: ウェーバ (Wb)
- 磁束密度: テスラ (T)
- インダクタンス: ヘンリー (H)
- セルシウス温度: セルシウス度 (°C)
- 光束: ルーメン (lm)
- 照度: ルクス (lx)
- 放射性核種の放射能: ベクレル (Bq)
- 吸収線量: グレイ (Gy)
- 線量当量: シーベルト (Sv)
- 酵素活性: カタール (kat)
SI接頭語(完全対応)
Hyper-NovaはすべてのSI接頭語に対応しています:
| 接頭語 | 記号 | 倍数 | 10の累乗 |
|---|---|---|---|
| クエタ (quetta) | Q | 1,000,000,000,000,000,000,000,000,000,000 | 10³⁰ |
| ロナ (ronna) | R | 1,000,000,000,000,000,000,000,000,000 | 10²⁷ |
| ヨタ (yotta) | Y | 1,000,000,000,000,000,000,000,000 | 10²⁴ |
| ゼタ (zetta) | Z | 1,000,000,000,000,000,000,000 | 10²¹ |
| エクサ (exa) | E | 1,000,000,000,000,000,000 | 10¹⁸ |
| ペタ (peta) | P | 1,000,000,000,000,000 | 10¹⁵ |
| テラ (tera) | T | 1,000,000,000,000 | 10¹² |
| ギガ (giga) | G | 1,000,000,000 | 10⁹ |
| メガ (mega) | M | 1,000,000 | 10⁶ |
| キロ (kilo) | k | 1,000 | 10³ |
| ヘクト (hecto) | h | 100 | 10² |
| デカ (deca) | da | 10 | 10¹ |
| デシ (deci) | d | 0.1 | 10⁻¹ |
| センチ (centi) | c | 0.01 | 10⁻² |
| ミリ (milli) | m | 0.001 | 10⁻³ |
| マイクロ (micro) | μ | 0.000001 | 10⁻⁶ |
| ナノ (nano) | n | 0.000000001 | 10⁻⁹ |
| ピコ (pico) | p | 0.000000000001 | 10⁻¹² |
| フェムト (femto) | f | 0.000000000000001 | 10⁻¹⁵ |
| アト (atto) | a | 0.000000000000000001 | 10⁻¹⁸ |
| ゼプト (zepto) | z | 0.000000000000000000001 | 10⁻²¹ |
| ヨクト (yocto) | y | 0.000000000000000000000001 | 10⁻²⁴ |
| ロント (ronto) | r | 0.000000000000000000000000001 | 10⁻²⁷ |
| クエクト (quecto) | q | 0.000000000000000000000000000001 | 10⁻³⁰ |
SI併用単位
SIと併用される非SI単位も完全サポート:
時間:
- 分 (min) = 60 s
- 時 (h) = 60 min = 3600 s
- 日 (d) = 24 h = 86,400 s
長さ:
- 天文単位 (au) = 149,597,870,700 m
平面角および位置角:
- 度 (°) = (π/180) rad
- 分 (') = (1/60)° = (π/10,800) rad
- 秒 (") = (1/60)' = (π/648,000) rad
面積:
- ヘクタール (ha) = 1 hm² = 10⁴ m²
体積:
- リットル (L, l) = 1 l = 1 dm³ = 10³ cm³ = 10⁻³ m³
質量:
- トン (t) = 10³ kg
- ダルトン (Da) = 1.660,539,066,60(50) × 10⁻²⁷ kg
エネルギー:
- 電子ボルト (eV) = 1.602,176,634 × 10⁻¹⁹ J
- ネーパ (Np)
比の対数:
- ベル (B)
- デシベル (dB)
情報量
- bit, byte, KB, MB, GB, TB, PB, EB, ZB, YB
通貨(外部API連携)
- USD, EUR, JPY, CNY, GBP, CHF, AUD, CAD, 等
その他
- 画素: px, dpi
- 人数: person, people
- 速度: m/s, km/h, mph, knot
- その他の組立単位: すべてのSI基本単位と組立単位の組み合わせに対応
カスタム単位の定義
独自の単位を定義することも可能です。例えば、日本でおなじみの「東京ドーム」単位:
// Volume次元を持つ新単位「東京ドーム」を定義
unit TokyoDome: Volume = 1_240_000 m^3
// 使用例
pool_volume = 5 TokyoDome
print(pool_volume -> m^3) // 6200000 m^3
物理量の記号表記
Hyper-Novaでは、物理量の記号(質量 'm'、時間 't' など)を斜体で表記できないため、シングルクォーテーションを先頭に付ける規約を採用しています。
// 物理量の記号は 'symbol のように表記
'm = 10 kg // 質量 m
't = 5 s // 時間 t
'v = 'd / 't // 速度 v = 距離 d / 時間 t
'F = 'm * 'a // 力 F = 質量 m × 加速度 a
// 運動エネルギーの計算例
'E_k = 0.5 * 'm * 'v^2
これにより、変数名と物理量の記号を明確に区別できます。
物理次元型のメリット
- 安全性: 単位の不一致によるバグ(例: メートルとフィートを足し算、NASA火星探査機の事故を防ぐ)をコンパイル時に排除
- 物理的整合性: 静的型付けを極限まで押し進め、物理的な整合性までコンパイラが保証
- 自動単位推論: 演算結果の単位を自動的に推論
- 自動変換: 次元が同じ単位間での自動変換
- 可読性: コード自体が物理的な意味を明確に表現
5. コレクション
5.1 List (リスト)
順序付きの可変シーケンスです。
基本操作
-
作成:
scores = [90, 85, 100] -
アクセス:
scores[0] -
変更:
scores[0] = 95
デュアルシンタックス(簡潔構文とメソッド構文)
Hyper-Novaはリスト操作に対して、2つのスタイルをサポートします。
scores = [90, 85, 100]
// スタイルA: 簡潔構文
scores new [95] // 末尾に追加
scores new [92] to 1 // インデックス1に挿入
scores del 2 // インデックス2を削除
scores remov 85 // 値85を削除
// スタイルB: メソッド構文
scores.append(95) // 末尾に追加
scores.insert(1, 92) // インデックス1に挿入
scores.remove_at(2) // インデックス2を削除
scores.remove_value(85) // 値85を削除
// どちらも同じ動作
リストメソッド一覧
list = [1, 2, 3, 4, 5]
// 追加・挿入
list.append(6) // 末尾に追加 → [1,2,3,4,5,6]
list.insert(0, 0) // index 0に挿入 → [0,1,2,3,4,5,6]
list.extend([7, 8]) // 複数追加 → [0,1,2,3,4,5,6,7,8]
// 削除
list.remove_at(0) // index 0を削除 → [1,2,3,4,5,6,7,8]
list.remove_value(5) // 値5を削除 → [1,2,3,4,6,7,8]
list.clear() // 全削除 → []
// 検索
exists = list.contains(3) // true/false
idx = list.find(3) // 見つかった場合index、なければEmpty
count = list.count(2) // 値2の出現回数
// 並び替え
list.sort() // 昇順ソート
list.reverse() // 逆順
// その他
size = list.length() // 要素数
is_empty = list.is_empty() // 空かどうか
first = list.first() // 最初の要素(空ならEmpty)
last = list.last() // 最後の要素(空ならEmpty)
チェーンメソッド
result = [5, 2, 8, 1, 9]
.append(3)
.sort()
.reverse()
.first()
print(result) // 9
5.2 Map (マップ)
キーと値のペアです。波括弧 {} で定義します。
基本操作
-
作成:
data = {"key": "value"} -
アクセス:
data["key"] -
値の更新:
data["key"] = "newValue"
デュアルシンタックス(簡潔構文とメソッド構文)
data = {"name": "Taro", "age": 25}
// スタイルA: 簡潔構文
data new ["city": "Tokyo"] // 追加
data["age"] = 26 // 更新
exists = data in "name" // 存在確認
// スタイルB: メソッド構文
data.set("city", "Tokyo") // 追加・更新
data.remove("age") // 削除
exists = data.has("name") // 存在確認
value = data.get("name") // 取得(なければEmpty)
マップメソッド一覧
map = {"a": 1, "b": 2, "c": 3}
// 追加・更新
map.set("d", 4) // キー"d"を追加
map.update({"e": 5, "f": 6}) // 複数追加
// 削除
map.remove("a") // キー"a"を削除
map.clear() // 全削除
// 検索
exists = map.has("b") // キー存在確認
value = map.get("b") // 値取得(なければEmpty)
value = map.get("z", default: 0) // デフォルト値付き
// イテレーション
keys = map.keys() // キーのリスト
values = map.values() // 値のリスト
items = map.items() // [key, value]のリスト
// その他
size = map.length() // 要素数
is_empty = map.is_empty() // 空かどうか
forループ
Map型をforループで使用すると、key と value 変数が自動的に提供されます。
for speed_map:
print(key + "の速度は" + value)
5.3 Enum (列挙型)
enum State {
Start,
Stop,
}
6. 制御フロー
6.1 条件分岐
if condition:
...
elif other:
...
else:
...
6.2 ループ
特殊変数 i の完全仕様
重要: i は完全に予約された変数名であり、forループ内の特殊変数としてのみ使用可能です。それ以外の場所では絶対に使用できません。
// OK: forループ内でのみ使用可能
for 5:
print(i) // 0, 1, 2, 3, 4
// ERROR: forループ外では絶対に使用不可
i = 10 // コンパイルエラー: 'i' is a reserved loop variable
fn some_function:
i = 5 // コンパイルエラー: 'i' cannot be declared
send i
class MyClass:
int: i = 0 // コンパイルエラー: 'i' cannot be used as property
Pythonのforで傷ついた心を癒やす
Hyper-Novaの for ループは、Pythonの for で「なんでいちいち書かないといけないの?」と感じた部分を徹底的に改善しました。
Pythonの問題:
# Pythonでは、インデックスと要素の両方が欲しい場合
for index, item in enumerate(items):
print(f"{index}: {item}")
# enumerate()って何?初心者には難しい...
Hyper-Novaの解決策:
// Hyper-Nova: 何も考えなくていい
for items:
print(index + ": " + i)
// 勝手に index と i (要素) がある!
これは確かに「好きになっちゃう」わ!😍 面倒な宣言なしで、欲しいものがそこにある。これぞ「実家のような安心感」。
自動提供される変数
| ループタイプ | 提供される特殊変数 | 説明 |
|---|---|---|
for N: |
i |
0からN-1までのカウンタ |
for list: |
i, index
|
i=要素, index=0始まりインデックス |
for map: |
key, value
|
マップのキーと値 |
ループの詳細
// パターン1: カウント
for 5:
print(i) // i = 0, 1, 2, 3, 4(カウンタ)
// パターン2: リスト
scores = [90, 85, 100]
for scores:
print(i) // i = 90, 85, 100(要素そのもの)
// インデックスも欲しい場合
for scores:
print(index + ": " + i)
// 出力: 0: 90, 1: 85, 2: 100
// パターン3: Map
data = {"name": "Taro", "age": 25}
for data:
print(key + " = " + value)
// 出力: name = Taro, age = 25
拡張構文(明示的な変数名)
i の代わりに任意の変数名を使いたい場合:
// 基本構文(i を使用)
for scores:
print(i)
// 拡張構文(任意の変数名)
for score in scores:
print(score) // i と同じ動作
// インデックス付き
for idx, score in scores:
print(idx + ": " + score)
ネストループ
matrix = [[1, 2], [3, 4], [5, 6]]
// 内側のループでも i を使える(スコープが分離)
for matrix:
print("Row " + index)
for i: // ここの i は内側の行の要素
print(" " + i)
なぜ i は予約語なのか?
i は完全に予約された変数名であり、これには明確な設計理由があります。
重要な制限:
-
iという変数名は Mathブロックの中 と forループの中 でのみ特別に許可されています - これら以外の場所(通常のコード)では
iを使ってはいけません
// OK: forループ内
for 5:
print(i) // 0,1,2,3,4
// OK: Mathブロック内
math:
\sum_{i=0}^{n} i^2
// ERROR: 通常のコード内では使用不可
i = 10 // コンパイルエラー!
問題: 予約しない場合の混乱
// もし i を予約しなかった場合...
i = 100 // ユーザーが定義した変数
for 5:
print(i)
// 期待: 0,1,2,3,4 (カウンタとして)
// 実際: 100,100,100,100,100 ?(既存の変数として)
// どちらの動作が正しいのか、コンパイラは判断できない
このような曖昧性を完全に排除するため、i はforループとMathブロック専用の特殊変数として予約されています。
推奨: 2つの構文パターン
Hyper-Novaは、初心者からプロまで対応できるよう、2つのパターンを提供します。
// パターン1: 簡潔構文(i を使用)- 初心者向け
for 5:
print(i) // 0,1,2,3,4
// パターン2: 明示的構文(任意の変数名)- プロ向け
for n in range(5):
print(n) // 0,1,2,3,4
// どちらも同じ動作
// 初心者は簡潔構文、プロは明示的構文を使える
設計の利点
-
曖昧性ゼロ:
iの意味が常に明確(ループカウンタ/要素、または数式の変数) -
段階的学習: 最初は簡潔に、慣れたら明示的に書ける
-
可読性: コードレビュー時に
iの役割が即座に理解できる -
Pythonの改善: Pythonでの面倒な宣言を不要にし、傷ついた心を癒やす
-
While:
while count < 5:
6.3 Match (パターンマッチ)
list 内の存在確認や Empty チェックに強力です。範囲指定は to を使用します。
match value {
case Pattern1: ...
case Pattern2: ...
case else: ...
}
-
Not Match:
notmatch(パターンに一致しない場合に実行)notmatch user_input { case banned_list: "その名前は使えません!" case else: "登録完了!" } -
範囲構文:
if val == 5 to 10:(5以上10以下)
6.4 自然言語アサーション (check)
Hyper-Novaの「読みやすさ」をさらに高めるため、自然言語に近い形で条件チェックやアサーションを記述できます。
基本的なチェック
check user is not Empty message "ユーザーは必須"
check item in data_list message "アイテムが見つかりません"
check quantity is greater than 0 message "数量は正の値であるべき"
拡張された範囲構文 (to キーワード)
既存の to キーワードを再利用・拡張し、より直感的な範囲表現を提供します。
// 以下のチェック
check value is to 10 // value ≤ 10
check value is from 5 // value ≥ 5
check value is from 5 to 10 // 5 ≤ value ≤ 10
// 比較演算子
check age is greater than 18 // age > 18
check age is less than 65 // age < 65
契約プログラミングとの組み合わせ
fn calculate_tax(age, income):
expect:
// 年齢は18歳以上65歳以下でなければならない
check age is from 18 to 65 message "年齢が対象外です"
check income is greater than 0 message "収入は正の値であるべき"
if income is to 50000: // 収入が50000以下の場合
send income * 0.10
else:
send income * 0.20
自然言語アサーションのメリット
- 直感的な記述: 英語の表現に近く、非開発者でも理解しやすい
-
一貫性の向上:
toキーワードの再利用で言語設計に統一感 -
エラーメッセージの付与:
messageキーワードでカスタムエラーメッセージを定義でき、デバッグが容易
7. 関数 (fn)
fn キーワードで定義します。戻り値は send で返す必要があります。
fn add(a, b):
send a + b
引数の型は静的に推論・チェックされます。引数はカンマ区切りで変数名のみを書きます。デフォルト引数、名前付き引数、可変長引数はサポートしません。
重要: 関数はすべての実行パスで send が必須です。書き忘れるとコンパイルエラーになります。
fn divide(a, b):
if b == 0:
send Err("ゼロ除算")
else:
send a / b
// どちらのパスでもsendがある
型推論の詳細
関数は最初の呼び出し時に引数の型が推論され、以降はその型で固定されます。
fn add(a, b):
send a + b
// 最初の呼び出しで型が決定
result1 = add(1, 2) // int + int → int
// この時点で add は fn(int, int) -> int として確定
result2 = add(5, 10) // OK: int + int
result3 = add(1.5, 2.5) // コンパイルエラー
// Error: Type mismatch. Expected (int, int), got (float, float)
型変換
int: a = 10
float: b = 3.14
// 暗黙的変換は行われない
// result = a + b // コンパイルエラー
// 明示的変換が必要
result = float(a) + b // OK: 13.14
// as キーワードでも可能
result = a as float + b
7.1 契約プログラミング (Design by Contract)
Hyper-Novaは契約プログラミング(DbC)をサポートし、関数の事前条件と事後条件を明示的に定義できます。
事前条件 (expect)
関数が実行される前に満たされているべき条件です。主に引数に対する制約を定義します。責任は**関数を呼び出す側(クライアント)**にあります。
fn calculate_ratio(numerator, denominator):
expect:
// 分母はゼロ以外でなければならない
denominator <> 0
// 両方とも正の値でなければならない
numerator > 0 && denominator > 0
send numerator / denominator
呼び出し元が calculate_ratio(10, 0) のようにゼロを渡した場合、expect で定義された契約が破られ、実行時エラー(またはコンパイル時エラー)が発生します。
事後条件 (ensure)
関数が実行された後に満たされているべき条件です。主に戻り値やオブジェクトの状態に対する保証を定義します。責任は関数を実装する側にあります。
fn increase_value(value):
new_value = value + 1
ensure:
// 戻り値は必ず元の値より大きいことを保証
new_value > value
// 戻り値は Err ではないことを保証
new_value <> Err
send new_value
関数が ensure の条件を満たさずに send した場合、実装が契約違反となり、エラーが発生します。
契約プログラミングのメリット
- エラーの早期発見: バグの原因を「呼び出し側(事前条件違反)」または「実装側(事後条件違反)」に明確に切り分け、問題を早期に発見できます。
- ドキュメントとしての機能: コード自体が関数の意図(何を受け取り、何を返すか)を明確に説明する、信頼性の高いドキュメントとして機能します。
-
堅牢性の向上:
ErrやEmptyのチェックに加えて、値の範囲や関係性といったより複雑な制約をコンパイラやランタイムにチェックさせることで、Hyper-Novaの**「安全性」の哲学**をさらに深めることができます。
8. タスクと並列処理 (task)
タスクは並列実行の単位です。
8.1 定義
task Worker(param):
result = param * 2
send result as worker_result
8.2 send とグローバルスコープ
-
send value: 関数の戻り値を返します。 -
send value as name: タスク内で使用すると、valueをプログラム全体(真にグローバル)にnameという名前で公開します。 - 後続のタスクやメインスレッドから、その変数を参照可能になります。
send as の競合ルール
重要: 同名の変数を複数のタスクから send as することはコンパイルエラーです。
task TaskA():
send 10 as data // OK
task TaskB():
send 20 as data // コンパイルエラー!
// Error: Global variable 'data' is already published by TaskA
使い分けガイド
| 用途 | 推奨方法 | 例 |
|---|---|---|
| タスク間データ受け渡し |
-> パイプライン |
task A() -> task B() |
| メインから結果取得 |
<- 取得演算子 |
result <- task A() |
| グローバル設定値 | send as |
send config as APP_CONFIG |
ベストプラクティス
// ✅ 良い例: 明示的なデータフロー
task LoadData():
data = read_file("data.csv")
send data
task ProcessData(input):
result = process(input)
send result
result <- task LoadData() -> task ProcessData()
// ❌ 悪い例: グローバル汚染
task LoadData():
send read_file("data.csv") as global_data
task ProcessData():
send process(global_data) as global_result
8.3 データフロー演算子 (-> と <-)
タスク間のデータの流れを明示的に定義し、グローバルスコープの汚染を防ぎます。
パイプライン接続 (->)
タスクAの出力をタスクBの入力に直接接続します。
// データフローの例
task SensorRead() -> task Calculate() -> task Visualize()
結果の取得 (<-)
メインスレッドまたは他のタスクが、タスクの結果を明示的に取得します(awaitのような機能)。
// 結果取得の例
final_data <- task Visualize() // Visualizeの終了を待って結果を取得
// 変数に格納
result <- task Calculate(10, 20)
print("計算結果: " + result)
データフロー演算子のメリット:
- 依存関係の明確化: タスク間の依存関係が視覚的に明確
- グローバル汚染の防止: 必要なデータのみを明示的に流す
- デバッグの容易化: データの流れを追跡しやすい
8.4 タスク競争 (race task)
複数のタスクを競争させ、最も速い結果を採用します。
// 3つの異なるサーバーに並列で問い合わせる
race task CheckServers():
task ServerA():
send network_query(serverA) as dataA
task ServerB():
send network_query(serverB) as dataB
task ServerC():
send network_query(serverC) as dataC
// 最初にsendされた結果をwinnerに格納
winner <- win result from CheckServers() timeout 2s
タスク競争の特徴:
-
競争グループ:
race taskで複数タスクをグループ化 -
結果の確定:
win result fromで最初の結果を採用 -
タイムアウト:
timeoutで時間制限を設定(超過時はEmptyまたはErr("Timeout")) - 自動停止: 最初の結果が確定したら、他のタスクは即座に停止
メリット:
- 応答性の極大化: 複数の選択肢から最速の結果を取得
- 耐障害性: 一部のタスクが失敗しても他が成功すればOK
- リアルタイムシステム: ゲーム、金融取引、分散システムで威力を発揮
8.5 タスクの障害回復 (on Crash restart)
タスクの異常終了時の自動再起動を定義します。Err(論理エラー)とCrash(システム障害)を明確に区別します。
// Crash時のみ再起動する(最大3回まで)
task DataWorker() on Crash restart(max: 3):
// パターンA: 想定内のエラー (Err)
if file_exists("data.txt") == false:
// 正常なエラー報告。タスクは終了し、再起動しない
send Err("FileNotFound")
// パターンB: 致命的な障害 (Crash)
if db.is_connected() == false:
// 異常事態。即座に再起動がかかる
send Crash("DBConnectionLost")
send "Success"
ErrとCrashの使い分け
| 型 | 意味 | 動作 | 用途 |
|---|---|---|---|
Err |
論理エラー | タスクは正常終了し、エラー値を返す。再起動しない | ファイルがない、入力値が不正、ユーザーが見つからない等 |
Crash |
システム障害 | タスクは異常終了。on Crashが定義されていれば再起動 |
DB接続切れ、メモリ不足、外部APIのダウン等 |
メリット:
- 意図の明確化: 「報告」と「リトライ」を型レベルで区別
- 自動回復: 一時的な障害からの自動復旧
- 堅牢性: ミッションクリティカルなシステムでの可用性向上
8.6 アトミック操作 (atomic do ... end)
send as のグローバル汚染のリスクを減らすために、「このブロック内の処理は、他のタスクに邪魔されずに一気に最後まで実行されるよ!」という保証を与える機能。
task update_score:
// この間に他のタスクにscoreを書き換えられない
atomic do
GlobalScore: score = score + 1
end
アトミック操作のメリット:
- Race Conditionの防止: 複数タスクが同時にアクセスしても、データの整合性を保証
-
明示的な同期:
atomicブロックで同期が必要な箇所が明確になる - デッドロック回避: コンパイラが自動的にデッドロックを検出
8.7 タスクの自動再実行 (@retry)
ネット通信とか、たまに失敗する処理があるよね。それを自動でリトライしてくれるデコレータ!
@retry(times: 3, delay: 500ms)
task download_file:
// 失敗しても最大3回、500ミリ秒待って再実行してくれる!
result = http_get("https://example.com/data.json")
if result == Err:
send Err("Download failed")
send result
@retry のパラメータ:
-
times: 最大リトライ回数 -
delay: リトライ間の待機時間(ms, s, min 単位で指定可能) -
backoff: (オプション) 指数バックオフ戦略 (linear,exponential)
// 指数バックオフの例
@retry(times: 5, delay: 100ms, backoff: exponential)
task connect_database:
// 1回目: 100ms、2回目: 200ms、3回目: 400ms... と待機時間が増加
...
リトライデコレータのメリット:
- 耐障害性の向上: 一時的なネットワークエラーやAPI制限を自動的に処理
- コードの簡潔化: try-catchやループを書く必要がない
- 運用の安定性: リアルタイムシステムやクラウドサービスで威力を発揮
8.8 パイプライン演算子 (|)
bashのパイプのように、データを次々と関数に渡していける演算子!
// データをパイプラインで処理
data = [1, 2, 3, 4, 5]
| filter(x -> x > 2)
| map(x -> x * 2)
| reduce((a, b) -> a + b)
print(data) // 24 (3*2 + 4*2 + 5*2 = 6 + 8 + 10 = 24)
// もっとシンプルな例
data | print // そのまま出力
パイプライン演算子の特徴:
- 左から右への flow: データの流れが視覚的に明確
- 関数チェーン: メソッドチェーンのような書き方
- 可読性: 複雑な処理が読みやすくなる
// パイプラインとタスクの組み合わせ
result <- task LoadData()
| task ProcessData()
| task Validate()
| task Save()
パイプライン演算子のメリット:
- 宣言的な記述: 何をするかが明確
- Unix哲学: 小さな関数を組み合わせる設計思想
- デバッグしやすい: 各ステップを個別にテスト可能
9. クラス (OOP)
9.1 定義
単一継承のみをサポートします。class キーワードで定義します。Hyper-Novaは複雑な継承階層を避け、シンプルで追跡しやすいコード構造を推奨します。
クラス内クラスの自動継承
重要: クラス内で定義されたクラスは、自動的に親クラスを継承します。
class Animal:
fn eat:
print("もぐもぐ")
fn animal_sound:
print("動物の音")
// Humanは自動的にAnimalを継承
class Human:
// eatは継承される
// animal_soundをオーバーライド
fn animal_sound:
print("こんにちは")
// 新しいメソッド
fn think:
print("考え中...")
// 使用例
human = Human()
human.eat() // 出力: もぐもぐ(継承)
human.animal_sound() // 出力: こんにちは(オーバーライド)
human.think() // 出力: 考え中...
明示的継承(括弧で指定)
外部で継承する場合は括弧で親クラスを明示します。
// 明示的継承
class Cat(Animal):
fn meow:
print("ニャー")
cat = Cat()
cat.eat() // OK: Animalから継承
cat.meow() // OK
多段継承
class Animal:
fn eat:
print("食事")
class Mammal:
fn breathe:
print("呼吸")
class Human:
fn speak:
print("話す")
// Humanは Animal -> Mammal -> Human と継承
human = Human()
human.eat() // Animal から継承
human.breathe() // Mammal から継承
human.speak() // Human 自身のメソッド
9.2 アクセス修飾子
-
public(パブリック): どこからでもアクセス可能。 -
protected(プロテクテッド): 同パッケージおよびサブクラスからアクセス可能。 -
(指定なし): 同パッケージ内のみアクセス可能。 -
private(プライベート): 同クラス内のみアクセス可能。
9.3 プロパティと初期化
プロパティは型宣言と共に定義し、init メソッドで初期化します。
class User:
// プロパティ定義
public string: name
private int: age
public boolean: is_active = true
// コンストラクタ(初期化メソッド)
fn init(name, age):
self.name = name
self.age = age
// メソッド
fn greet:
print("Hello, I am " + self.name)
10. モジュールと可視性
10.1 インポート
import キーワードまたはブロックを使用します。
import "math_lib.hn"
import:
"network_lib.hn"
"ui_lib.hn"
10.2 エクスポート
デフォルトでは定義は内部のみです。export ブロックを使用して外部に公開します。
export:
public_api
PublicClass
11. エラーハンドリング
Hyper-Novaには例外(Exception)はありません。エラーは Err 型の値として扱われます。
11.1 Err型
Err はエラー専用の型で、エラーメッセージとエラーであることが記載されています。
send Err("ゼロ除算エラー") as divError
11.2 errクラス(グローバルエラー名前空間)
Err を send すると、グローバルな err クラスの傘下に格納されます。err クラスの継承は不可能です。
task Divide():
if dataB == 0:
send Err("ゼロ除算") as divError
else:
send dataA / dataB as result
task C():
if err.divError == Err:
send Err("Divide失敗") as finalError
else:
send result * 2 as finalResult
11.3 エラーの確認
エラーかどうかを確認する際は、err クラスに問い合わせます。
if err.divError == Err:
print("エラーが発生しました")
12. メモリ管理と所有権
Hyper-NovaはGCとライフタイムベースの手動管理のハイブリッドシステムを採用しています。
12.1 ガベージコレクション (GC)
デフォルトでGCが有効です。通常の変数はGCによって自動管理されます。
12.2 所有権と借用システム
パフォーマンスが重要な領域や外部連携では、所有権ベースの手動管理を使用できます。
所有権 (@)
データがメモリ上に存在する権利を持つことを示します。変数がスコープを抜けると、メモリは自動で解放されます(RAII)。
@user = User("Taro") // userが所有権を持つ
// スコープを抜けると自動的に解放される
借用 (~)
所有権を持つデータに対して、一時的にアクセス権限を得ます。データは参照されるだけで、所有権は移動しません。
@data = [1, 2, 3, 4, 5]
~ref = data // 借用(読み取り専用)
print(ref[0]) // OK
可変借用 (~mut)
データの内容を変更できるアクセス権を得ます。
@data = [1, 2, 3]
~mut ref_mut = data // 可変借用
ref_mut[0] = 100 // OK: 変更可能
12.3 借用チェッカーと排他アクセスルール
コンパイラに組み込まれた借用チェッカーは、すべての借用に対して以下のルールをチェックします。
コア排他ルール(Single Mutability Rule)
データが生存している間、常に以下のうちいずれか一つしか存在できません:
-
一つの可変借用 (
~mut) - データを変更可能なアクセス権は同時に一つだけ -
多数の不変借用 (
~) - データを読み取り専用のアクセス権は同時にいくつでも許可
@user = User("Taro")
// OK: 不変借用は複数可
~ref1 = user
~ref2 = user
// OK: 可変借用(単独)
~mut ref_mut = user
ref_mut.rename("Jiro")
// ERROR: 既にref_mutが存在するため、他の借用は禁止
// ~ref_err = user // コンパイルエラー!
12.4 ライフタイム ('L)
すべての所有権と借用には、ライフタイム(データが有効な期間)がコンパイラによって関連付けられます。
ライフタイムのルール
- 参照先より長く生存できない: 借用のライフタイムは、それが指す所有者のライフタイムより長くは存在できません
- スコープ依存: ライフタイムは、変数が宣言されたスコープや関数呼び出しによって決まります
ライフタイムの明示的指定
// 'a はライフタイムの識別子
// 戻り値の参照は引数dataの生存期間と同じであることを保証
fn get_ref<'a>(@data: ~'a List) -> ~'a int:
send data[0] // データの最初の要素の参照を返す
// エラー例: ダングリングポインタ
fn create_dangling() -> ~int:
@local_data = [1, 2, 3] // local_dataのライフタイムは関数内
send ~local_data[0]
// ERROR: 戻り値のライフタイムは関数の外側だが、
// local_dataは関数終了時に破棄される
12.5 並列タスクと借用の安全性
タスク間でのデータ安全性を保証するため、以下のルールが適用されます。
タスク安全性のルール
| ルール | 制限内容 | 理由 |
|---|---|---|
| sendの制限 | 借用されたデータはsendできない |
借用はローカルなライフタイムに依存するため、グローバルに公開すると無効な参照を引き起こす |
| taskへの入力 | taskの引数は、値のコピーまたは所有権の移動のみ | 借用をタスクに渡すと、並列実行中にデータ競合を引き起こす可能性がある |
これらのルールにより、タスク間でやり取りされるデータは常にイミュータブルなコピーか独立した所有権を持つことが保証され、並列実行環境においてもゼロ・データ競合が達成されます。
12.6 その他のメモリ操作
-
参照 (Reference/Read-only):
^(例:x ^ @address) -
アドレス指定:
@ADDRESS -
範囲指定:
@START.END(例:@2000.2007は2007番地を含む) -
解放:
drop variable_name
12.1 安全な外部連携 (safe external)
C/C++などの既存ライブラリと連携する際のメモリ安全性を確保します。
外部型定義
// 外部ライブラリの構造体を定義
safe external struct CLibPoint {
int: x
int: y
}
// 外部関数の宣言
safe external fn c_calculate(~ data) -> int
安全な借用
外部関数にポインタを渡す際、借用構文 ~ を使用して安全性を保証します。
point = CLibPoint { x: 10, y: 20 }
// 外部関数に安全に借用を渡す
result = c_calculate(~ point)
安全な外部連携のメリット:
- Hyper-Novaの安全性を維持: 外部コードとの連携でも安全性を担保
- 低レベルパフォーマンス: Cエコシステムへの簡単なアクセス
- ライフタイム管理: 外部型にライフタイム情報を付与
13. ローレベル (アセンブリ)
13.1 インラインアセンブリ
簡単なアセンブリ命令を1行で書けます。
asm(xor r12, r12)
13.2 アセンブリブロック
複数行のアセンブリコードを書く場合は asm: ブロックを使用します。
asm:
global _start
_start: mov rbp, 1
L: xor r12, r12
mov rax, rbp
mov bl, 3
div bl
test ah, ah
jnz C5
mov rsi, F
mov dx, 4
call W
inc r12
14. 演算子(キッチンの包丁セット)
読みやすさ重視でコンパクト、でも切れ味鋭く:
// 算術
+ // 加算 - // 減算
* // 乗算 / // 除算
% // 剰余 ^ // 累乗
++ // インクリメント
-- // デクリメント
// 比較
== // 等号 <> // 不等号
> // 大きい < // 小さい
>= // 以上 <= // 以下
=== // 型も同じ(厳密)
// 論理
&& // and || // or
! // not !& // nand
^^ // xor
// 代入
= // 代入 += // 加算して代入
-= // 減算して代入 *= // 乗算して代入
/= // 除算して代入
// メモリ関連(詳細は「12. メモリ管理」を参照)
~ // 借用(Borrow)
^ // 参照(Reference/Read-only)
@ // アドレス指定
15. 関数・タスクと send(値は必ず出口へ)
関数もタスクも すべての分岐で send が必須。書き忘れたらコンパイルで「それ、どこに返すの?」とツッコミが入ります。
fn divide(a, b):
if b == 0:
send Err("ゼロ除算")
else:
send a / b
- 引数: カンマ区切りで変数名のみ。デフォルト/名前付き/可変長は無し。シンプルこそ美徳。
-
タスクの返却:
send value as nameはグローバルなイミュータブル値として公開。 -
スコープ: ローカルは関数・タスクを出たら消滅。
sendしたものだけが生き残る。
実行フロー例(グローバル八卦炉)
task A():
send 10 as dataA
task B():
send 0 as dataB
task Divide():
if dataB == 0:
send Err("ゼロ除算") as divError
else:
send dataA / dataB as result
task C():
if err.divError == Err:
send Err("Divide失敗") as finalError
else:
send result * 2 as finalResult
- タスクは並列で走り、
sendされた瞬間にグローバルに公開。 -
err.<name>はグローバルに放たれたエラーの正門。
16. Empty と Err(“nullお断り”同盟)
Hyper-Nova には null は存在しません。代わりに二枚看板:
- Empty: 「値がないけど正常」。比較は可、演算や表示は不可。
-
Err: 「何か壊れた」。メッセージ付きで、グローバルに放つと
err名前空間に居座る。
fn find_user(id):
if id > 0:
send "User" + id
else:
send Empty
match find_user(0) {
case Empty: print("ユーザーが見つかりません")
case else: print("ユーザー: " + user)
}
fn get_optional_field(user):
if user.has_nickname():
send user.nickname
else:
send Empty // ないのは正常
- 「見つからない/空っぽ」は Empty、「失敗した」は Err を使い分け。
- 明示型変数には Empty を代入できません(型の筋トレ)。
17. 数式サポート(TeX)
Hyper-NovaはTeXによる数式記述をネイティブにサポートしています。科学計算、数学的アルゴリズムの実装、技術文書の生成などに活用できます。
17.1 数式ブロック (math:)
複数行の数式を記述する場合は math: ブロックを使用します。
math:
E = mc^2
\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}
17.2 インライン数式 (math())
コード内で数式をインラインで記述する場合は math() 関数を使用します。
// インライン数式の使用例
equation = math(\alpha + \beta = \gamma)
print("方程式: " + equation)
// 計算と組み合わせ
quadratic = math(x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a})
17.3 用途
- 科学計算: 物理定数や数式の明確な表現
- アルゴリズム実装: 数学的アルゴリズムの可読性向上
- ドキュメント生成: 技術文書やレポートへの数式埋め込み
- 教育: 数学的概念の明示的な記述
18. 正規表現(今後の拡張予定)
正規表現機能は計画されていますが、具体的な構文はまだ定義されていません。目標は「ちょー簡単に書ける」正規表現です。
19. ドキュメントテスト (doc check)
コードのドキュメントコメントを実行可能なテストとして機能させます。
19.1 テストコメント
関数やクラスのドキュメントコメント内に #check: プレフィックスを付けてテストコードを記述します。
fn add(a, b):
// #check: add(1, 1) == 2
// #check: add(-5, 5) == 0
// #check: add(0, 100) == 100
send a + b
fn divide(a, b):
// #check: divide(10, 2) == 5
// #check: divide(10, 0) // -> Err("ゼロ除算")
if b == 0:
send Err("ゼロ除算")
else:
send a / b
19.2 自動テスト
コンパイラオプションまたは専用コマンドでドキュメントテストを実行します。
hn check-docs # すべてのドキュメントテストを実行
hn check-docs myfile.hn # 特定ファイルのみテスト
19.3 メリット
- ドキュメントとテストの統合: コード例が常に最新かつ正しいことを保証
- 信頼性の高いドキュメント: 実行可能なコード例がドキュメントに埋め込まれる
- リファクタリングの安全性: 関数変更時にドキュメントの不整合を自動検出
20. インテリジェント変数名静的解析
20.1 コンセプト
コンパイラは変数名から意図を推測し、不適切な型使用を警告します。
20.2 警告ルール
// 警告例1: 時間関連の変数に小さすぎる型
int16: time = 1000
// ⚠️ Compiler Warning:
// Variable 'time' suggests time-related data.
// int16 (max: 32,767) may overflow for millisecond timestamps.
// Consider using int32 or int64.
// Continue anyway? (yes/no)
// 警告例2: カウンタに符号付き型
int: count = 0
count-- // 負の値になる可能性
// ⚠️ Compiler Warning:
// Variable 'count' suggests a counter.
// Consider using uint for non-negative values.
// 警告例3: IDに小さい型
int8: user_id = 1
// ⚠️ Compiler Warning:
// Variable 'user_id' suggests an identifier.
// int8 (max: 127) may be insufficient for ID ranges.
// Consider int32 or int64.
20.3 対象となる変数名パターン
| パターン | 推奨型 | 理由 |
|---|---|---|
time, timestamp, *_time
|
int64, uint64
|
タイムスタンプはミリ秒で大きくなる |
count, counter, *_count
|
uint, uint32
|
カウンタは非負 |
id, *_id
|
int32, int64, string
|
IDは大きな範囲が必要 |
index, idx, *_index
|
uint, int
|
インデックスは非負または符号付き |
size, length, *_size
|
uint, uint32
|
サイズは非負 |
price, amount, *_price
|
Decimal, float64
|
金額は精度が重要 |
20.4 警告の抑制
// 警告を抑制する場合
@suppress_warning("small-type")
int16: time = 1000 // 警告なし
// または型エイリアスで意図を明示
alias SmallTime = int16
SmallTime: time = 1000 // 警告なし(エイリアスで意図が明確)
21. コンパイラの動作オプション
21.1 警告レベル
# デフォルト(全警告を表示)
hn compile myfile.hn
# 警告を抑制
hn compile myfile.hn --no-warnings
# 警告をエラーとして扱う
hn compile myfile.hn --warnings-as-errors
# 変数名静的解析のみ無効化
hn compile myfile.hn --no-name-analysis
21.2 インタラクティブモード
# 警告時に確認プロンプトを表示
hn compile myfile.hn --interactive
# 自動的に全てYesで進める
hn compile myfile.hn --yes
21.3 主要なコンパイラフラグ
| フラグ | 説明 |
|---|---|
--no-warnings |
すべての警告を抑制 |
--warnings-as-errors |
警告をエラーとして扱う |
--no-name-analysis |
変数名静的解析を無効化 |
--interactive |
警告時に確認プロンプト表示 |
--yes |
すべての確認に自動的にYes |
--check-docs |
ドキュメントテストのみ実行 |
仕様書終了