Open28

📝 Nim for TypeScript Programmers 日本語訳

torish14torish14

目次

比較 変数 三項演算子 無名関数
This アロー関数 コンパイル時の関数実行 サーバーサイドレンダリング
クライアントサイドレンダリング Svelte Parcel CommonJS
WebGL GUI Electron Webview React
その他の GUI フレームワーク WebAssembly ビルドモード 最小化
難読化 JavaScript の相互接続性 相互接続のためのプラグマ 相互接続性テンプレート
正規表現 Nim 用の HTML/CSS WYSYWIG エディタ NodeJS での実行 BigInt
Fetch ベストプラクティス その他
免責事項!

非公式で、作業中です! これは代用品でしかないです。拡張にご協力ください。このガイドには不正確な点があるかもしれません。このガイドは、ある程度の中級の知識を想定しています。

公式チュートリアルはこちらでご覧になれます:

  • Nim チュートリアル (パート 1)
  • Nim チュートリアル (パート 2)
  • Nim チュートリアル(パート 3)

公式マニュアルでは、言語の概要が説明されています:

  • Nim マニュアル

公式ライブラリドキュメントには、Nim の標準ライブラリのドキュメントがあります:

  • Nim 標準ライブラリ
torish14torish14

比較

特徴 TypeScript Nim
これを使用して書かれた TypeScript Nim
ライセンス Apache MIT
バージョン 3.x 1.x
静的、"確かに正しい" 型 静的、強力、推論
メタプログラミング デコレータに制限がある テンプレート、マクロ
int8/16/32/64 型
float32/float64 型
Char 型
Subrange 型
JSON 型
Regex 型
オプション 型
オペレーターオーバーローディング
カスタムオペレーター
ランタイムチェック
サイド・エフェクトトラッキング
Enum 型
不変性 制限的、読み取り専用 キーワード
関数の引数は不変 可変 不変
完全な DOM API
どこでも console.log() ❓ コンパイラが文句言う
どこでも console.assert() ❓ コンパイラが文句言う
NodeJS 統合
ジェネリクス
型インターフェース
クロージャ
オブジェクト指向
メソッド
例外処理
無名関数
アロー関数
配列内包表記
フォーマットされた文字列リテラル
FFI ✅ JS のみ ✅ C/C++/JS
Async
正規表現
自己文書化コマンド
パッケージ公開
パッケージマネージャー
コード自動フォーマッター ✅ NPM 経由 ✅ Nimpretty
ファイル拡張 ✅ .ts、.tsx ✅ .nim、.nims
torish14torish14

変数

新しい変数の作成は、varletconst を使います。Nim は不変量とコンパイル時の関数実行を備えています。const は TypeScript とはちがって、真の意味で不変です。

特徴 const let var
ランタイム いいえ ✅ はい ✅ はい
コンパイル時間 ✅ はい いいえ いいえ
不変 ✅ はい ✅ はい いいえ
自動初期化 ✅ はい ✅ はい ✅ はい
再割り当て可能 いいえ いいえ ✅ はい
割り当てが必要 ✅ はい ✅ はい いいえ
グローバルにすることができる ✅ はい ✅ はい ✅ はい

ゼロから始める場合、学習中に var を使用してもエラーは発生しませんので、学習を進めることができます。

torish14torish14

三項演算子

conditional ? "result0" : "result1"

⬆️ TypeScript ⬆️ ⬇️ Nim ⬇️

if conditional: "result0" else: "result1"

三項演算子が単なる if.else のインラインであることにお気づきでしょうか。

torish14torish14

無名関数

var myfunc = (argument1: number, argument2: number) => {
    return argument1 + argument2
};

console.log( myfunc(1, 2) )

⬆️ TypeScript ⬆️ ⬇️ Nim ⬇️

var myfunc = ( proc (argument1, argument2: int): int = argument1 + argument2 )

echo myfunc(1, 2)

Nim では、無名関数とは、名前を持たず、大括弧で囲まれた関数のことです。

torish14torish14

This

Nim の this には固定の命名規則がないので、self を使っているコードも見かけます。Nim は最初の引数であることにしかこだわらないので、thisself はデフォルトで 不変です。

type Cat = object

proc purr(this: Cat) = echo "Purr Purr"         # 'this' を使っている
proc dance(self: Cat) = echo "Tappity Tappity"  # 'self' を使っている

let garfield = Cat()

garfield.purr()   # ゴロゴロ
garfield.dance()  # タタタン

# うん、this/self はインスタンスだよ
proc himself(self: Cat): Cat = return self
echo garfield == garfield.himself()  # true
torish14torish14

アロー関数

Nim にはアロー関数というものがあり、これは通常の関数に対する糖衣構文です。アロー関数を使うには、import sugar をする必要があります。

上の猫の例をアロー関数を使うように変換してみましょう:

import sugar

type Cat = object

let purr = (this: Cat) => echo "Purr Purr"         # `this` を使っている
let dance = (self: Cat) => echo "Tappity Tappity"  # `self` を使っている

# 呼び出し構文は同じです

引数を渡す必要がない場合は、渡さないでください:

import sugar

let purr = () => echo "Purr Purr"  # 引数なし

purr()  # ゴロゴロ

アロー関数にプラグマを使用することができます:

let functionName = () {.inline.} => 42
let another_name = () {.inline.} => 42

(インラインプラグマも参照)

アロー関数に関数名とプラグマを使用することができます:

function_name(argument0, argument1: int) {.noSideEffect.} => argument0 + argument1
let variab = (argument0, argument1: int) {.noSideEffect.} => argument0 + argument1

(noSideEffect プラグマも参照)

torish14torish14

コンパイル時の関数実行

Nim はコンパイル時に関数を実行することが可能なので、コンパイル時にバックエンド的なコードを実行し、実行時にフロントエンドで使用することができます。コンパイル時の FFI も可能です。コンパイル時に動作するコードや NimScript のほとんどは、フロントエンドでも動作する傾向があります。

torish14torish14

サーバーサイドレンダリング

SCF

Nim Source Code Filters (SCF)は、テンプレートのサーバーサイド描画を行うための標準ライブラリのテンプレート機構です。

SCF は *.nimf というファイル拡張子を持ち、#?stdtmpl というシバングで始まります。内部では、ロジックは通常の Nim コードですが、接頭辞に # が付いており、テンプレートはそのまま(# なし)で書かれています。

SCF は通常、文字列を返す関数です。これらの関数は通常の文字列操作や書式設定が可能です。これらは通常の Nim コードにコンパイルされ、優れたパフォーマンスを提供します。

serverside.nimf
#?stdtmpl
#func generateXML(name, age: string): string =
<xml>
    <name>$name</name>
    <age>$age</age>
</xml>
serverside_includer.nim
include "serverside.nimf"

echo generateXML("Nim", "16 (in 2021)")

# prints:
# <xml>
#     <name>Nim</name>
#     <age>16 (in 2021)</age>
# </xml>

Karax

また、Karax フレームワークはサーバーサイドのレンダリングを行います(nimble install karax でインストール)。

ikarax_helloworld_serside.nim
import karax/[karaxdsl,vdom]

writeFile "app.html", $(block: buildHtml(tdiv):
  h1: text"Hello World"
  p: text"Compile:  nim r file.nim ")
karax_helloworld_serverside_bulma.nim
include prelude
import karax / [karaxdsl, vdom]

writeFile "example.html", $(block: buildHtml(tdiv):

  link(href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css", rel="stylesheet")

  section(class="section"):
    tdiv(class="container"):
      h1(class="title"):
        text"Welcome"

      button(class="button is-success"): text"Example"

      ol(class="ol"):
        li: text"Item 0"
        li: text"Item 1"

      code(class="code"): text"""echo "Hello World" """
)
torish14torish14

クライアントサイドレンダリング

Karax はクライアントサイドレンダリングもできますし、ローカルの開発サーバーも含まれていますし、他にもとても素晴らしいものがあります。

このサンプルは、Karax karun ツールを使ってウェブブラウザ上でコンパイルし、実行することができます。

karax_helloworld_clientside.nim
include karax/prelude
setRenderer func: auto = buildHtml(h1): text"Hello World"
Karax
proc createDom(): VNode = 
  # クライアントサイドレンダリング
  result = buildHtml(tdiv):
  # テキストだけ
  text "Hello Web Frontend World"
  # <aside class="foo" id="bar">
  aside(class="foo", id="bar"):
    # <img src="cat.png" alt="cat">
    img(src="cat.png", alt="cat")
    # 属性なしの <span> 
    span:
      # テキストだけ
      text "Clickable <button> inside a <span>"
      # <button>
      button(onclick = () => console.log(""Hi!")):
        # テキストだけ
        text "Say Hi!"
      # いくつかのロジック
      for numbeeeeer in 0..9: console.log(number)
      # クリックできる <a>
      a(onclick = () => console.log("Clicked!"))

# サーバーサイド、ルーティング、バリデーション、イベントなど
setRenderer createDom 
torish14torish14

WebGL GUI

nimx は WebGL をターゲットとしたクロスプラットフォームな GUI フレームワークで、GUI を WebGL にコンパイルすることができます。

torish14torish14

Webview

また、クロスプラットフォームの小さな(1 つの .h ファイル)、高速(C コード)なウェブビューを使用することができます:

  • WebGui
  • nim 用の Webview

WIISH も同様のクロスプラットフォームな WebView を提供しており、他のターゲットにも対応しています。

torish14torish14

More GUI frameworks

もし、JavaScript のみの GUI であることが必須ではなく、Windows/Linux/Mac で動作する GUI が必要なだけであれば、さらに多くの選択肢を試すことができます:

  • nimx
  • NimQL
  • uibuilder.nim
  • wNim
  • NiGui
  • illwill
  • gintro
  • UI
  • nimgui
torish14torish14

WebAssembly

以下のものを使って、Nim のコードを WebAssembly にコンパイルすることができます:

  • Clang WASM target OR Emscripten OR NLVM

WebAssembly へのコンパイルの詳細については、こちらをご覧ください:

  • NimES
  • nlvm
  • Nim WebAssembly の例
  • wasmrt
torish14torish14

Build modes

コードが本番環境に対応できるようになったら、コンパイルコマンドに -d:release-d:danger を追加して、リリースビルドを作成することができます。

特徴 リリースビルド デバッグビルド
速度 高速 低速
ファイルサイズ 小さい 大きい
最適化
トレースバック
ランタイムチェック
コンパイル時チェック
assert
doAssert
torish14torish14

Minification

Nim はデフォルトでコンパイルされた JavaScript を minify しません

Nim は通常、リリース用にビルドする際に非常に小さなファイルサイズにコンパイルします。不要なコード除去のおかげで、使用されるシンボルだけがコンパイルされ、他のシンボルはリリースビルドには存在しません。例えば、あるモジュールをインポートしたが使わなかった場合、リリースビルドには存在しません(ターミナルにヒントが表示され、使われていないインポートがあることが分かります)。

Nim は空白をインデントとして使用します。基本的には Nim がコンパイルした JavaScript ファイルで問題ありません。

あるいは、他の minifier ソフトウェアを使って、JavaScript の後処理をすることもできます。

torish14torish14

難読化

Nim はデフォルトでコンパイルされた JavaScript を難読化 しません

もし、コンパイルされた JavaScript を難読化したい場合は、{.exportc.} プラグマを使って名前のマングリングを制御し、難読化として使用することができます。

var variable {.exportc: "lkfnjmgkw3du4905r3q2ep8n4urfp34w2efltgvepotik132qm0".} = false
proc funct() {.exportc: "kl34jgo9liw35e4atr8i30q2rk1fipkpfrsdofir93o2qujfoks".} = echo 42

以下にコンパイルする:

var lkfnjmgkw3du4905r3q2ep8n4urfp34w2efltgvepotik132qm0 = false;

function kl34jgo9liw35e4atr8i30q2rk1fipkpfrsdofir93o2qujfoks() {
  rawEcho("42");
}

変数 名や 関数 名といった人間にとって親しみやすい名前を使いながら、コードは難読化された名前にコンパイルされます。

Nim は名前を見失いません。難読化された名前は任意のランダムアルゴリズムで生成できます。

また、他の難読化ソフトウェアを使って、JavaScript の後処理を行うこともできます。

torish14torish14

JavaScript の相互接続性

Nim は JavaScript と直接相互に接続することができます。ここでは、独自のライブラリを作成する方法を説明します。

性能面での追加コストはなく、コンパイラはあなたが望むコードを出力するだけです。

できるだけ最小限の例で、あまり有用ではありませんが、できるだけシンプルに説明します:

func log(arg, arg2: SomeNumber) {.importjs: """console.log(#, #)""".}
  • # は、もしあれば、インデックス0から始まるインデックスごとの引数に置き換えられます。
  • @ は、もしあれば、カンマで区切られた残りのすべての引数で置き換えられます。
  • 文字列または Regex の単一の $ をエスケープするために $$ を使用します。
  • $1 は、現在の関数名で置き換えられます。
  • 引数の型と戻り値の型はNimの型、std libの型、または独自のカスタム型です。
  • importcppimportjs として使われることがあります。
  • 関数の戻り値として openArray を使うと、seq の代わりに array を返します。
  • バニラ JavaScript の console.log() などでコンソールに出力する。
  • バニラ JavaScript の (function(){ ...})(); で DOM が準備されていることを確認する。
  • バニラ JavaScript の "use strict"; でストリクトモードを強制する。
  • ImportJs プラグマ
  • 関数に引数がない場合、@ は何も生成しない(空)。
  • 関数に引数がない場合、# はエラーを発生させる。
  • なお、@ の場合はカンマが自動的に挿入されますが、# の場合はカンマを追加する必要があります。
  • パターンには複数行の文字列を使用することができます。
  • パターンには strformat を使用することができますが、strformat をインポートする必要があります。

この小さな例をJavaScriptのターゲットにコンパイルしてみよう:

lognumberss.nim
func log(arg, arg2: SomeNumber) {.importjs: """console.log(#, #)""".}

log(42, 9)

コンソールで実行する

nim js -d:danger lognumbers.nim

lognumbers.js を生成する

lognumbers.js
console.log(42, 9);

名前付き引数を使用する必要がある場合は、{.emit.} プラグマを使用することができます:

func functionName(namedArgument: int) = {.emit: """console.log( `namedArgument`);""".}
torish14torish14

相互接続のための有用なプラグマ

  • {.importjs.} で JavaScript をインポートする。
  • {.compiletime.} コンパイル時にコードを実行するようにします。
  • {.exportc.} エスケープ名のマングリング(名前修飾のこと、デバッグや難読化などのため)。
  • {.emit.} 渡された引数で直接コードを実行します。
  • {.varargs.} 関数に複数の引数を強制的に受け取らせる。
  • {.hint.} コードを生成しない、コンパイル時の人間に優しい色付きメッセージ。
  • 警告。コードを生成しないコンパイル時に表示される、人間に優しい色のメッセージ。
  • {.codegendecl.} コードジェネレータで直接コードを出力します。
  • {.noinit.} 暗黙の変数自動初期化を回避する。
  • {.discardable.} デバッグ、クイックプロトタイピングなどのために)未使用の戻り値を破棄できるようにする。
  • {.noreturn.} 関数が決して返さないように強制する
  • {.asmnostackframe.} 関数に結果を持たせないようにします。関数のresultを自動注入しないようにします。
  • {.injectStmt.}を使用すると便利です。現在のモジュールの他のすべてのステートメントの前にコードをインジェクトします。
  • プラグマは単なるマクロなので、自分で作ることができます。

もっと見る:

  • static: / static() コードのブロックをコンパイル時に強制的に実行させる。
  • when コンパイル時 if
  • 実際のコードの例としては、jsre モジュールがあります。
  • jsconsole モジュールで、実際のコード例を見ることができます。
torish14torish14

相互接続性テンプレート

  • 独自の JavaScript ライブラリを作成するために編集できる Nim の「コードテンプレート」擬似コードです:

func functionName(argument: SomeNumber | string | bool): SomeNumber {.importjs: """
(function () {
  "use strict";

  console.log("CODE HERE");
  console.log(#);
  return 42;

})(); """, exportc: "functionName".}  ## Documentation Comment.


func functionName2(namedArgument: SomeNumber | string | bool): SomeNumber = {.emit: """

  console.log("CODE HERE");
  console.log( `namedArgument` );
  return 42;

""", exportc: "functionName2".}  ## Documentation Comment.