@kokiito0926/logos
v0.0.1
Published
論理記号と数学記号を用いて数式のように書けるプログラミング言語です。
Maintainers
Readme
λόγος(ロゴス)
論理記号と数学記号を用いることができるプログラミング言語です。
コードの大部分をAIに書いてもらったが、それなりに機能しているとは思う。
設計思想
function・引数リスト・return・セミコロンのような定型的な記述を減らし、数式をそのまま定義として書けるようにすることを目指す。
Unicodeの論理・数学記号は入力しやすいとは限らないが、意味を圧縮して読みやすくできる可能性がある。
AIがコードを生成する場面では、記述量の削減にも役立つことを期待している。
基本構文
定義と暗黙的引数
定義には ≔(または ASCII の :=)を使う。
定義名には英字・数字・アンダースコアを使える。
answer ≔ 42
answer_copy := 42
square ≔ α²右辺で使われるギリシャ文字(α〜ω)は、自動的に関数の引数になります。
引数を明示して宣言する必要はありません。
square ≔ α²
add ≔ α + β
distance ≔ √((α.x - β.x)² + (α.y - β.y)²)上のコードは次の JavaScript にコンパイルされます。
const square = α => (α ** 2);
const add = (α, β) => (α + β);
const distance = (α, β) => Math.sqrt((((α.x - β.x) ** 2) + ((α.y - β.y) ** 2)));通常の識別子は暗黙的引数にはなりません。π は Math.PI として扱われ、ギリシャ文字を使わない定義は定数になります。
関数本体は式だけで構成されるため、return を書かなくても式の値がそのまま戻り値になります。
pi_value ≔ π
hypotenuse ≔ √(α² + β²)const pi_value = Math.PI;
const hypotenuse = (α, β) => Math.sqrt(((α ** 2) + (β ** 2)));数学定数とリテラル
| Logos | JavaScript |
| --- | --- |
| π | Math.PI |
| ∞ | Infinity |
| ⊤ | true |
| ⊥ | false |
| ∅ | new Set() |
演算子
| Logos | 用途 | 変換例 |
| --- | --- | --- |
| + - * / | 四則演算 | |
| = ≠ < > ≤ ≥ ≡ ≈ | 比較(= ≡ は厳密等価、≈ は almostEqual) | α ≈ β → almostEqual(α, β) |
| ← | 代入・再代入 | α ← β → (α = β) |
| ¬ | 否定 | ¬α → (!α) |
| ∧ ∨ ⇒ ⇔ ⊕ | 論理(積・和・含意・同値・排他的論理和) | α ⇒ β → (!α \|\| β), α ⊕ β → (α !== β) |
| ↦ → | 明示的アロー関数(マッピング) | x ↦ x² → x => (x ** 2) |
| ∘ | 関数合成 | f ∘ g → ((...args) => f(g(...args))) |
| \|> | パイプライン | x \|> f(a) → f(x, a) |
| ^ ² ³ ⁴ ⁻¹ | べき乗(任意の Unicode 上付き指数) | α² → (α ** 2), α⁻¹ → (α ** -1) |
| √ | 平方根 | √(α) → Math.sqrt(α) |
| ⌊ ⌋ ⌈ ⌉ | 床・天井関数 | ⌊α⌋ → Math.floor(α), ⌈α⌉ → Math.ceil(α) |
| ! | 階乗(後置) | 5! → factorial(5) |
| { e \| x ∈ S } | 集合の内包表記 | { x² \| x ∈ S } → new Set([...S].map(x => (x ** 2))) |
| ∈ ∉ | 集合の帰属・非帰属 | α ∈ set → set.has(α) |
| ∩ ∪ ∖ × | 集合演算 | A ∩ B → 共通部分 Set, A × B → 直積 Set |
| ⊆ ⊂ | 部分集合・真部分集合 | A ⊆ B → [...A].every(x => B.has(x)) |
| ‥ | 範囲 | 1‥10 → range(1, 10), 0‥<n → range(0, n, true) |
| ∀ ∃ ∄ | 量化(全称・存在・非存在) | ∀ α ∈ A : P(α) → [...A].every(α => P(α)) |
| ∑ ∏ | 総和・総積 | ∑ α ∈ A : f(α) → [...A].reduce((acc, α) => acc + f(α), 0) |
| ∫ | 定積分・不定積分 | ∫₀¹ α² dα → simpson(0, 1, α => (α ** 2)) |
| ∂ | 偏微分 | ∂(α² + β²)/∂α → partial((α, β) => ((α ** 2) + (β ** 2)), 0) |
| ∇ | 勾配(ナブラ) | ∇(α² + β²) → gradient((α, β) => ((α ** 2) + (β ** 2))) |
| ′ ″ | 導関数・二階導関数 | (α²)′ → α => (2 * α) |
| ∮ | 周回積分 | ∮ (1/α) dα → contour(α => (cdiv(1, α))) |
| ⟦ ⟧ | アイバーソンの括弧 | ⟦α ≥ 18⟧ → ((α >= 18) ? 1 : 0) |
| μ | 最小化演算子(while) | μ α (α² ≥ 100) → (() => { let α = 0; while (!(((α ** 2) >= 100))) α++; return α; })() |
| ⇒ ⊤ | if / else(場合分け) | α ≥ 0 ⇒ α / ⊤ ⇒ −α → ((α >= 0) ? α : (-α)) |
| − | 単項マイナス(U+2212 対応) | −α → (-α) |
| # | 行コメント | x ≔ 1 # 説明 |
その他: ()(優先順位)、α.x(プロパティ参照)、α[0](添字アクセス)、比較の連結(0 ≤ α ≤ 10 → (0 <= α) && (α <= 10))、文字列リテラル("…" / '…'、\n などのエスケープ対応)。
下付き添字
Unicode の下付き添字は添字アクセスとして扱われる。
ギリシャ文字の暗黙引数に対しても同様に動作します。
dist_sub ≔ α₁ + β₂const dist_sub = (α, β) => (α[1] + β[2]);集合演算
∅ で作った空集合や Set を対象に使える。
in_both ≔ α ∈ A ∩ B
is_sub ≔ A ⊆ B集合演算子の優先順位は算術演算子より低く、比較演算子より高い(α ∈ A ∩ B は α ∈ (A ∩ B))。∪ より ∩ が高い優先順位なので、A ∪ B ∩ C は A ∪ (B ∩ C)。
量化
構文は ∀/∃/∄ 変数 ∈ 集合 : 本体。
束縛変数は暗黙引数には含まれません。
本体はコロンの後に改行とインデントで続けて書くこともできます。
対象は配列と Set のどちらでも使えます。
all_adult ≔ ∀ α ∈ users : α.age ≥ 18
has_adult ≔ ∃ α ∈ users : α.age ≥ 18
no_minor ≔ ∄ α ∈ users : α.age < 18const all_adult = [...users].every(α => (α.age >= 18));
const has_adult = [...users].some(α => (α.age >= 18));
const no_minor = ![...users].some(α => (α.age < 18));総和・総積
構文は ∑/∏ 変数 ∈ 集合 : 本体。
生成コードは reduce を用います(総和は初期値 0、総積は初期値 1)。
total ≔ ∑ α ∈ numbers : α
product ≔ ∏ α ∈ numbers : αconst total = [...numbers].reduce((acc, α) => acc + α, 0);
const product = [...numbers].reduce((acc, α) => acc * α, 1);パイプライン演算子
左辺の式を右辺の関数の第一引数として渡します。
result ≔ numbers |> filter(isPrime) |> map(square) |> sumconst result = sum(map(filter(numbers, isPrime), square));- 右辺が関数参照
f→f(x) - 右辺が関数呼び出し
f(a, b)→f(x, a, b) - 右辺がメソッド
obj.f(a)→obj.f(x, a) - 右辺がアロー関数
(y ↦ y * 2)→((y => (y * 2)))(x) - 右辺が合成
f ∘ g→(((...args) => f(g(...args))))(x)
パイプライン演算子の優先順位は最も低いため、x + 1 |> f は f(x + 1) と解釈されます。
注:
filter,map,sumなどの関数は JavaScript 側で定義してください。
インデントブロック
≔ の後に改行とインデントを続けると複数行ブロックを定義できます。
ブロック内でローカル変数を定義し、最後の式を暗黙的に戻り値とします。
distance ≔
γ ≔ α.x - β.x
δ ≔ α.y - β.y
√(γ² + δ²)const distance = (α, β) => {
const γ = (α.x - β.x);
const δ = (α.y - β.y);
return Math.sqrt(((γ ** 2) + (δ ** 2)));
};- 暗黙引数はブロック全体の自由変数(ギリシャ文字)から自動収集され、ブロック内の定義名は除外されます。
- ブロック内の定義は依存順に並べ替えられるため前方参照も可能。循環参照はエラーになります。
- ブロック内には定義のほか、式文(関数呼び出しや再代入
←など)も書けます。最後の式文が戻り値になります。 - 本体が式 1 つだけの場合は従来どおりインラインのアロー関数に短縮されます。
- インデントは一貫している必要があります(タブは 4 スペース換算)。
制御フロー
条件分岐(if / else)・条件の値化・while ループを、英語キーワードではなく論理記号で書けます。
if / else(⇒・⊤)
ブロックの末尾に 条件 ⇒ 結果 の並びを書くと、ネストした三項演算子に変換されます。
最後の ⊤ ⇒ 結果 が else 節です。
abs ≔
α ≥ 0 ⇒ α
⊤ ⇒ −αconst abs = α => {
return ((α >= 0) ? α : (-α));
};⊤は else を表します。⊤ ⇒ Aは単にAに変換されます。- 3 つ以上の分岐もネストした三項演算子になります(例:
α < 0 ⇒ 0/α > 10 ⇒ 10/⊤ ⇒ α)。 ⊤を伴わない単一のP ⇒ Aは、論理含意(!P || A)としてコンパイルされます。
アイバーソンの括弧(⟦ ⟧)
⟦P⟧ は条件 P を値(真なら 1、偽なら 0)に変換します。[ ] は配列リテラルに使うため、二重ブラケット ⟦ ⟧ としています。
adult ≔ ⟦age ≥ 18⟧const adult = ((age >= 18) ? 1 : 0);最小化演算子(μ・while)
μ α (P(α)) は、P(α) が成り立つ最小の α ≥ 0 を探索します(while ループ)。
上の例は n = 10 になります。
n ≔ μ α (α² ≥ 100)const n = (() => { let α = 0; while (!(((α ** 2) >= 100))) α++; return α; })();変数の更新を伴うループ(μ + ←)
μ の後に改行とインデントでブロックを続けると、←(再代入)による変数更新を伴う while ループになります。
ブロックには x ← 式 の更新文を並べ、最後の文はループの条件式(止まる条件)にします。
ループは条件が成り立つまで更新文を実行し、束縛変数 α の最終値を返します。
上の例は 1 + 2 + … + 10 = 55 を total に返します。
total ≔ μ s
s ← s + α
α ← α + 1
α > 10const total = (() => { let s = 0; let α = 0; while (!((α > 10))) { (s = (s + α)); (α = (α + 1)); } return s; })();μの束縛変数(上の例ではs)と、ブロック内で←により更新される変数は、ループ内のローカル変数として0で初期化されます。- 束縛変数が
←で更新されない場合は、自動的にα++されます(μ α (P(α))と同じ挙動)。 - ループ外のギリシャ文字変数(上の例の
α)は暗黙の引数として捕捉されます。
微積分記号(∫・∂・∇・′・∮)
∫(積分)、∂(偏微分)、∇(勾配)、′(導関数)、∮(周回積分)を数式のまま書けます。
∂・∇・′ は記号微分により厳密な導関数を生成します(多項式・積・商・冪・√・初等関数(連鎖律)に対応)。
式をそのまま微分できない場合は数値計算(有限差分)にフォールバックし、そのときだけ先頭に数値計算用ランタイムが注入されます。∫ は常に数値積分(シンプソン則)、∮ は複素平面上の数値積分(シンプソン則・n=2000)です。∮ を使うときは先頭に複素数ランタイムが注入されます。
area ≔ ∫₀¹ α² dα # 定積分(面積)
deriv ≔ ∂(α² + β²)/∂α # 偏微分(厳密)
grad ≔ ∇(α² + β²) # 勾配ベクトル(厳密)
prime ≔ (α²)′ # 導関数(プライム)const area = simpson(0, 1, α => (α ** 2));
const deriv = (α, β) => (2 * α);
const grad = (α, β) => [(2 * α), (2 * β)];
const prime = α => (2 * α);- 定積分:
∫下付き上付き f d変数。範囲は Unicode の下付き・上付き文字(₀,¹など)で書き、両方必要(片方だけはエラー)。dαのように続けて書けます(d αと空白を入れても可)。 - 不定積分: 範囲を省略すると
integrate(f)になり、(下限, 上限) => 定積分の関数を返します(F(0, 1)と呼び出すと定積分の値になります)。 - 偏微分:
∂式/∂変数。∂をまたぐ/は除算ではなく偏微分記号とみなされます。返り値は点を渡すと偏微分係数を返す関数です(deriv(2, 3)→4)。関数参照の場合は∂f/∂α→partial(f, 0)の数値計算になります。 - 勾配:
∇式(または関数参照∇f)は、全変数による偏微分を並べたベクトル場を返します(grad(2, 3)→[4, 6])。 - プライム(導関数):
f′は関数参照fの導関数(partial(f, 0))、f″は二階導関数です。(α²)′のように式に付けても書けます。その場合、式の最初のギリシャ文字(アルファベット順)で微分します。(α ↦ α²)′のようにインラインのアロー関数に付けると、本体を記号微分します。 - 初等関数の微分(連鎖律):
∂sin(u)/∂α→cos(u)·u′、∂cos(u)/∂α→−(sin(u))·u′、∂exp(u)/∂α→exp(u)·u′、∂ln(u)/∂α→u′/u。 - 一般の冪: 指数が変数に依存する場合
d(u^v)/dα = u^v·(v′·ln(u) + v·u′/u)(例:∂(α^α)/∂α→α^α·(ln α + 1))。指数が定数の場合は従来どおりv·u^(v−1)·u′(例:∂(α^β)/∂α→β·α^(β−1))。 - 周回積分:
∮ f dαは、複素変数αに関するfの単位円周(中心 0・半径 1)に沿った周回積分。dα(d αと空白を入れても可)は必須です。結果は複素数オブジェクト{ re, im }で返り、cre(z)・cim(z)で実部・虚部を取り出せます(例:cre(∮ (1/α) dα)≈ 0、cim(∮ (1/α) dα)≈ 2π)。被積分関数内の四則演算・冪乗・sin/cos/exp/ln/sqrtは複素数演算(cadd/csub/cmul/cdiv/cpowなど)に自動的に書き換えられます。コーシーの留数定理により、∮ (1/α) dα= 2πi、∮ (1/(α - 2)) dα= 0(極が円外)です。 - 複素数リテラル
i: 虚数単位iがそのままリテラルです(i→{ re: 0, im: 1 }、3i→{ re: 0, im: 3 })。2 + 3iのように四則演算・冪乗・単項マイナス・sin/cos/exp/ln/sqrt/√は、被演算子にiが含まれると複素数演算(cadd/csub/cmul/cdiv/cpow/cneg/csinなど)に自動的に書き換えられ、結果は複素数オブジェクト{ re, im }になります(例:2 + 3i→{ re: 2, im: 3 })。cre(z)・cim(z)で実部・虚部を取り出せます。
コマンドライン
.logos ファイルは CLI で JavaScript にコンパイルできます。
出力先を指定しない場合は、生成結果を標準出力に表示します。
$ npm install --global @kokiito0926/logos$ logos ./example/index.logos
$ logos ./example/index.logos --output ./example/index.js| オプション | 内容 |
| --- | --- |
| --output, -o | 生成した JavaScript の保存先 |
| --verbose, -v | トークン、AST、生成 JavaScript を表示 |
| --help, -h | ヘルプを表示 |
今後の構想
検討中の機能
- 絶対値
|x|:|α|→Math.abs(α)(|>パイプラインとは別トークンとして扱う) - 剰余演算
mod:α mod β→α % β
その他の構想
- Unicode数学スタイルの識別子: 太字 (
𝐀,𝐱)、イタリック (𝑓,𝑥)、筆記体 (𝒜,𝒳)、フラクトゥール (𝔄,𝔅) 等をそのまま識別子としてサポート - 型注釈構想 (TypeScript等へのコンパイル): 黒板太字 (
ℕ,ℤ,ℝ,ℂ) を型注釈として利用(例:age : ℕ)
