vim-jp / vimdoc-ja / vim9

vim9 - Vim日本語ドキュメント

メインヘルプファイルに戻る English | 日本語 | 編集
vim9.txt      For Vim バージョン 9.1.  Last change: 2025 Oct 06


                  VIM リファレンスマニュアル    by Bram Moolenaar


Vim9 script のコマンドと文法                                    Vim9 vim9

ほとんどの文法については eval.txt で解説されている。このファイルには Vim9
script の新しい文法と機能について書かれている。


1.  Vim9 script とは                    Vim9-script
2.  変更点                              vim9-differences
3.  新しいスタイルの関数                fast-functions
4.  型                                  vim9-types
5.  ジェネリック関数                    generic-functions
6.  名前空間、Import と Export          vim9script
7.  クラスとインターフェイス            vim9-classes
8.  理論的根拠                          vim9-rationale


------------------------------------------------------------------------------

  NOTE: この vim9.txt ヘルプファイルでは、vim9script で始まる Vim9 script の
        コードブロックは、Vim9 script の構文がハイライトされている。また、これ
        らはソースとして実行できるため、実行して出力内容を確認できる。ソースと
        して実行するには、:'<,'>source を使用する (:source-range を参照)。
        これは、V で行を視覚的に選択し、:so と入力することで実行できる。
        例えば、以下の Vim9 script で試してみてほしい: >vim9

                vim9script
                echowindow "Welcome to Vim9 script!"
<
        ソースコードとして提供すべきではないコード例もある。これらは、ソース
        コードとして提供可能な例を必要としない概念を説明しているものである。こ
        のようなコードブロックは、以下のように、汎用コード構文のハイライトで表
        示される:

                def ThisFunction()          # スクリプトローカル
                def g:ThatFunction()        # グローバル
                export def Function()       # import および import の自動ロー
                                            # ド用

==============================================================================

1. Vim9 script とは                                     Vim9-script


Vim script は、互換性の維持に気を配りながら成長してきた。そのため、古い悪しき
仕様を変更できないことが多い他、Vi との互換性に制約を受けて、より良い解決策を
採用できなくなっている。処理は遅く、実行するたびに各行のパースが行われている。

Vim9 script の主な目的は劇的な性能の向上である。これは、コマンドをより効率よく
実行できる命令にコンパイルすることで実現している。これにより、10 倍から 100 倍
の実行速度の向上が期待できる。

第 2 の目的は、Vim script 特有の文法を回避し、より一般的に使われる JavaScript
や TypeScript、Java のようなプログラミング言語に近づけることである。

パフォーマンスの向上は、100% の下位互換性を捨てることによってのみ達成しうるも
のである。例えば、関数の引数を辞書 "a:" から利用できるようにするためには、かな
りのオーバーヘッドが必要になる。そのため、Vim9 script では、この辞書が使用でき
なくなった。その他の違いは、エラーの処理方法など、より微細なものである。

Vim9 script は以下の場所で使用することができる:
- コマンド :def で定義された関数の中
- コマンド vim9script で始まるスクリプトファイルの中
- 上記のコンテキストで定義された自動コマンド
- コマンド修飾子 vim9cmd が先頭に付いたコマンド

Vim9 script ファイル内で :function を使用する場合、最高の scriptversion を
持つ旧来の構文が使用される。ただし、これは混乱を招く可能性があるため、推奨され
ない。

Vim9 script と旧来の Vim script を混在させることができる。古いスクリプトを書き
直す必要はなく、以前と同じように動作する。高速化が必要なコードには、いくつかの
:def 関数を使用するとよいだろう。

:vim9[cmd] {cmd}                                :vim9 :vim9cmd E1164
                Vim9 script の構文とセマンティクスを使用して {cmd} を評価、実
                行する。コマンドを入力する時や、旧来のスクリプトまたは関数内で
                便利である。

:leg[acy] {cmd}                                 :leg :legacy E1189 E1234
                旧来のスクリプト構文とセマンティクスを使用して {cmd} を評価、
                実行する。Vim9 script または :def 関数内でのみ便利である。
                Note {cmd} は旧来の式構文で解析されるため、ローカル変数を使用
                できないことに注意。

52.6 の Vim9 script の例を参照。
==============================================================================

2. 旧来の Vim script からの変更点                       vim9-differences

概要
                                                        E1146
Vim9 script と :def 関数を使用するときに最も頻繁に遭遇する違いの簡単な概要。
詳細は以下の通り:
- コメントは " ではなく、# で始める:
        echo "hello"   # コメント
- 行継続にバックスラッシュを使用する必要はほとんどない:
        echo "hello "
             .. yourName
             .. ", how are you?"
- 読みやすさを向上させるために、多くの場所で空白が必要である。
:let E1126 を使用せずに値を代入し、:var を使用して変数を宣言する:
        var count = 0
        count += 3
:final と :const を使用して、定数を宣言できる:
        final matches = []                # 後でこのリストに追加する
        const names = ['Betty', 'Peter']  # 変更できない
:final は :finally の略語として使用することはできない。
- 変数と関数は、デフォルトではスクリプトローカルである。
- 関数は引数の型と戻り値の型で宣言される:
        def CallMe(count: number, message: string): bool
:call なしで関数を呼び出す:
        writefile(['done'], 'file.txt')
- 古い Ex コマンドは使用できない:
        :Print
        :append
        :change
        :d  の直後に 'd' または 'p' が続く。
        :insert
        :k
        :mode
        :open
        :s  とフラグのみ
        :t
        :xit
- 一部のコマンド、特にフロー制御に使用されるコマンドは短縮できない。
  例えば、:throw を :th と書くことはできない。 vim9-no-shorten
- 波括弧変数は使用できない。
- コマンドの前の範囲にはコロンを前に付ける必要がある:
        :%s/this/that
- "@r" でレジスタを実行しても機能しない。先頭にコロンを追加するか、:exe を使
  用する:
        :exe @a
- 特に指定がない限り、最高の scriptversion が使用される。
- 式のマッピングを定義すると、式は定義されたスクリプトのコンテキストで評価され
  る。
- 文字列をインデックスする場合、インデックスはバイトではなく文字数でカウントさ
  れる:
  vim9-string-index
- 予想外の相違点がいくつかある: vim9-gotchas.

# から始まるコメント

旧来の Vim script ではコメントは、ダブルクォーテーションで始める。Vim9 script
ではコメントは # で始める。
        # 宣言
        var count = 0  # 出現回数

理由は、ダブルクォーテーションは文字列の始まりにもなり得るからである。多くの場
所、特に式の途中で改行がある場合、文字列とコメントの両方の後に任意のテキストが
続く可能性があるため、意味がわかりにくくなる。混乱を避けるために、# コメントの
みが認識される。これは、シェルスクリプトや Python プログラムの場合と同じであ
る。

Vi では、# は数字付きのテキストをリストするコマンドである。Vim9 script では、
そのために :number を使用できる。
        :101 number

読みやすさを向上させるには、コマンドとコメントを開始する # の間にスペースが必
要である:
        var name = value # コメント
        var name = value# エラー!
                                                        E1170
コメントを #{ で始めてはならない。これは旧来の辞書リテラルに似ており、混乱を招
く可能性がある場合にエラーが発生する。#{{ または #{{{ は問題ない。これらは折り
たたみを開始するために使用できる。

スクリプトファイルの読み込みを開始すると、Vim は vim9script コマンドが見つか
るまではそれが Vim9 script であることを認識しない。その時点までは、旧来のコ
メントを使用する必要がある:
        " 旧来のコメント
        vim9script
        # Vim9 のコメント

これは見た目が悪いので、vim9script を最初の行に置いた方がよい:
        vim9script
        # Vim9 コメント

旧来の Vim script では、# は代替ファイル名としても使用される。Vim9 script で
は、代わりに %% を使用する必要がある。## の代わりに %%% (すべての引数を表す)
を使用する。


Vim9 関数
                                                        E1099
:def で定義された関数はコンパイルされる。実行速度が何倍も速くなり、多くの場
合 10 から 100 倍になる。

関数が実行される前に、コンパイル時にすでに多くのエラーが見つかる。構文は厳密
で、読みやすく理解しやすいコードが強制される。

コンパイルは、以下のいずれかに遭遇した時に実行される:
- 関数が初めて呼び出されたとき
- 関数が定義された後にスクリプト内で :defcompile コマンドが見つかったとき
- 関数に :disassemble が使用されるとき
- コンパイルされた関数が関数を呼び出すか、関数参照として使用するとき (引数と戻
  り値の型をチェックできるようにするため)
                                                E1091 E1191
コンパイルが失敗した場合、次回の呼び出しでは再試行されず、代わりに次のエラーが
表示される: "E1091: Function is not compiled: {name}"
まだ作成されていないユーザーコマンドに遭遇すると、コンパイルは失敗する。この場
合、execute() を呼び出して実行時に呼び出すことができる。
        def MyFunc()
          execute('DefinedLater')
        enddef

:def には、:function のようなオプションはない: "range"、"abort"、"dict"、
"closure"。:def 関数は、エラーが発生すると常に中止する (コマンドに :silent!
が使用されている場合、またはエラーが :try ブロックでキャッチされた場合を除
く)。範囲は渡されず、"dict" 関数になることはできず、常にクロージャになることが
できる。
                                                vim9-no-dict-function E1182
"dict 関数" の代わりに Vim9 クラス (Vim9-class) を使用できる。辞書を明示的に
渡すこともできる:
        def DictFunc(self: dict<any>, arg: string)
           echo self[arg]
        enddef
        var ad = {item: 'value', func: DictFunc}
        ad.func(ad, 'item')

ただし、旧来の辞書関数を呼ぶことはできる:
        func Legacy() dict
          echo self.value
        endfunc
        def CallLegacy()
          var d = {func: Legacy, value: 'text'}
          d.func()
        enddef
                                                E1096 E1174 E1175
引数の型と戻り値の型を指定する必要がある。"any" 型を使用することができ、その場
合は旧来の関数と同様に実行時に型チェックが行われる。
                                                        E1106
引数は、他の言語と同様に、"a:" なしで名前でアクセスされる。"a:" 辞書や "a:000"
リストはない。
                        vim9-variable-arguments E1055 E1160 E1180
可変引数は最後の引数として定義され、名前を持ち、TypeScript と同様にリスト型を
持つ。例えば、数値のリストは次のようになる:
        def MyFunc(...itemlist: list<number>)
           for item in itemlist
             ...

関数の引数が任意 (デフォルト値を持つ) の場合、引数として v:none を渡すとデ
フォルト値が使用される。これは、デフォルト値を使用する引数の後に来る引数の値を
指定する場合に便利である。例:
        def MyFunc(one = 'one', last = 'last')
          ...
        enddef
        MyFunc(v:none, 'LAST')  # 第 1 引数は既定値の 'one' を使う

                                        vim9-ignored-argument E1181
引数 "_" (アンダースコア) は、引数を無視するために使用できる。これは引数は必要
ではないが、呼び出しに一致する引数を与える必要があるコールバックで最も役立つ。
例えば、map() を使用する場合、キーと値の 2 つの引数が渡され、キーを無視するに
は:
        map(numberList, (_, v) => v * 2)
"_" 引数を複数回使用してもエラーは発生しない。タイプを指定する必要はない。


関数と変数はデフォルトでスクリプトローカル
                                                        vim9-scopes
Vim9 script のスクリプトレベルで :function または :def を使用して新しい関
数を指定する場合、関数はスクリプトローカルになる。旧来のスクリプトで "s:" をプ
リフィックスとして付けるのと同じである。グローバル関数または変数を定義するに
は、"g:" プリフィックスを使用する必要がある。インポートされるスクリプト内の関
数やオートロードスクリプト内の関数を他の場所で使用するには、"export" を使用す
る必要がある。
        def ThisFunction()          # スクリプトローカル
        def g:ThatFunction()        # グローバル
        export def Function()       # import および import autoload 用
                                                E1058 E1075
:def 関数内で :function または :def を使用してネストされた関数を指定し、
名前空間が指定されていない場合、このネストされた関数は定義されているコードブ
ロックに対してローカルになる。文字列引数を持つ function() で使用することはで
きない。関数参照自体を渡すこと:
        def Outer()
          def Inner()
            echo 'inner'
          enddef
          var Fok = function(Inner)     # OK
          var Fbad = function('Inner')  # 動作しない

詳細: これは、"Inner" が実際には生成された名前を持つ関数への関数参照になるため
である。

関数内でスクリプトローカル関数を定義することはできない。ローカル関数を定義し
て、それをスクリプトローカル Funcref に割り当てることができる (スクリプトレベ
ルで宣言されている必要がある)。"g:" プリフィックスを使用してグローバル関数を定
義することは可能である。

関数を参照するときに "s:" または "g:" プリフィックスが使用されていない場合、
Vim は関数を次の場所で検索する:
- 関数スコープ内、ブロックスコープ内
- スクリプトスコープ内

import された関数は、:import コマンドのプリフィックスで見つかる。

スクリプトローカル関数の参照は "s:" なしでも使用できるため、"s:" プリフィック
スを使用する場合でも、名前は大文字で始まる必要がある。旧来のスクリプトで
は、"funcref" で参照できなかったため、"s:funcref" を使用できた。Vim9 script で
は参照できるため、組み込み関数と名前が干渉しないように、"s:Funcref" を使用する
必要がある。
                                                vim9-s-namespace E1268
Vim9 script レベルでは、プリフィックス "s:" の使用はサポートされていない。プリ
フィックスのないすべての関数と変数はスクリプトローカルである。

:def 関数では、"s:" の使用はスクリプトによって異なる。旧来のスクリプトのスクリ
プトローカル変数と関数は "s:" を使用するが、Vim9 script では "s:" を使用しな
い。これは、このファイルの残りの部分で見られる内容と一致する。

旧来の関数では、スクリプト項目に "s:" を以前と同様に使用する必要がある。スクリ
プトが Vim9 であるか旧来のものであるかは関係ない。

いずれの場合も、関数は使用前に定義する必要がある。つまり、関数が呼び出されると
き、:defcompile によってコンパイルされるとき、または関数を呼び出すコードがコ
ンパイルされるとき (戻り値の型を判断するため) である。

その結果、名前空間のない関数と変数は通常、スクリプト内で定義されるか、import
されるかのどちらかで見つかる。グローバル関数と変数はどこでも定義できる (どこで
定義されているか見つけるのは大変である! :verbose を使用して最後に設定された
場所を確認できる場合がよくある)。
                                                        E1102
グローバル関数は、ほぼいつでも定義および削除できる。Vim9 script では、スクリプ
トローカル関数はスクリプトが読み込まれたときに一度定義され、それ自体では削除ま
たは置換できない (スクリプトを再読み込みすることで削除または置換できる)。

関数をコンパイルし、(まだ) 定義されていない関数の関数呼び出しに遭遇した場合、
FuncUndefined 自動コマンドはトリガーされない。必要であれば、オートロード関数
を使用するか、旧来の関数を呼び出してそこで FuncUndefined をトリガーすること
もできる。


デフォルトでは Vim9 script の再読み込みにより関数と変数がクリアされる
                                                vim9-reload E1149 E1150
旧来の Vim script を 2 回目に読み込むと、何も削除されず、コマンドによって既存
の変数と関数が置き換えられ新しいものが作成され、削除されたものはそのまま残る。

Vim9 script を 2 回目に読み込むと、既存のスクリプトローカル関数と変数がすべて
削除されるため、クリーンの状態から開始できる。これはプラグインを開発中に新しい
バージョンを試したい場合に便利である。何かの名前を変更した場合、古い名前が残っ
ていることを心配する必要はない。

アイテムを保持したい場合は、以下を使用する:
        vim9script noclear

これは、再度読み込まれたときに、どこかの時点で finish コマンドを使用して抜け
出すスクリプトで使用することを想定してる。例えば、バッファローカルなオプション
が関数に設定されている場合、関数を複数回定義する必要はない:
        vim9script noclear
        setlocal completefunc=SomeFunc
        if exists('*SomeFunc')
          finish
        endif
        def SomeFunc()
        ....


:var、:final や :const で宣言する変数
                                vim9-declaration :var E1079
                                E1017 E1020 E1054 E1087 E1124
ローカル変数は :var で宣言する必要がある。ローカル定数は :final または
:const で宣言する必要がある。このセクションでは、両方を "変数" と呼ぶ。

変数はスクリプト、関数、またはコードブロックに対しローカルにすることができる:
        vim9script
        var script_var = 123
        def SomeFunc()
          var func_var = script_var
          if cond
            var block_var = func_var
          ...

変数は、それが定義されているブロックとネストされたブロック内でのみ参照できる。
ブロックが終了すると変数にはアクセスできなくなる:
        if cond
           var inner = 5
        else
           var inner = 0
        endif
        echo inner  # エラー!

宣言は早めに行う必要がある:
        var inner: number
        if cond
           inner = 5
        else
           inner = 0
        endif
        echo inner

単純な値の場合は、これは短くて高速である:
        var inner = 0
        if cond
           inner = 5
        endif
        echo inner
                                                        E1025 E1128
後続のコードから意図的に変数を隠すには、ブロックを使用できる:
        {
           var temp = 'temp'
           ...
        }
        echo temp  # エラー!

これはユーザーコマンドで特に役立つ:
        command -range Rename {
                 var save = @a
                 @a = 'some expression'
                 echo 'do something with ' .. @a
                 @a = save
            }

自動コマンドでの場合:
   au BufWritePre *.go {
                 var save = winsaveview()
                 silent! exe ':%! some formatting command'
                 winrestview(save)
           }

ただし、:def 関数を使用する方がおそらくうまく動作する。

                                E1022 E1103 E1130 E1131 E1133
                                E1134
型を指定して変数を宣言し初期化子を指定しない場合、値は false (boolの場合)、空
(文字列、リスト、辞書などの場合)、ゼロ (数値、any などの場合) に初期化される。
これは特に "any" 型を使用する場合に重要で、値はデフォルトで数値のゼロになる。
例えば、リストを宣言する場合、以下のように項目を追加できる:
        var myList: list<number>
        myList->add(7)

変数を null 値、例えば null_list、で初期化することは、変数を初期化しないこと
とは異なる。これはエラーが発生する:
        var myList = null_list
        myList->add(7)  # E1130: null リストには追加できません

                                                E1016 E1052 E1066

Vim9 script では:let は使用できない。既存の変数にコマンドなしで代入される。
グローバル変数、ウィンドウ変数、タブ変数、バッファ変数および Vim 変数も同様で
ある。これらは実際には宣言されていないためである。:unlet で削除することもで
きる。
                                                        E1065
変数の宣言に :va は使用できない。:var という完全な名前で記述する必要があ
る。読みやすさを確保するためである。
                                                        E1178
:lockvar はローカル変数では動作しない。代わりに :const および :final を
使用する。

exists() および exists_compiled() 関数は、ローカル変数または引数では動作し
ない。
                                E1006 E1041 E1167 E1168 E1213
変数、関数および関数の引数は、同じスクリプトファイル内で既に定義またはインポー
トされた変数や関数をシャドウすることはできない。
変数は Ex コマンドをシャドウすることがあるので、必要に応じて変数名を変更するこ
と。

グローバル変数はスクリプトレベルでも "g:" をプリフィックスとして付ける必要があ
る。
        vim9script
        var script_local = 'text'
        g:global = 'value'
        var Funcref = g:ThatFunction

グローバル関数には "g:" というプリフィックスを付ける必要がある:
        vim9script
        def g:GlobalFunc(): string
          return 'text'
        enddef
        echo g:GlobalFunc()
オートロード関数には "g:" プリフィックスは必要ない。

                                        vim9-function-defined-later
グローバル関数は "g:" プリフィックスなしでも呼び出すことができるが、コンパイル
時に存在している必要がある。"g:" プリフィックスを追加することで、関数を後から
定義できる。例:
        def CallPluginFunc()
          if exists('g:loaded_plugin')
            g:PluginFunc()
          endif
        enddef

以下のようにすると、"g:loaded_plugin" が存在しない場合でも、コンパイル時に
"PluginFunc" が存在しないというエラーが発生する:
        def CallPluginFunc()
          if exists('g:loaded_plugin')
            PluginFunc()   # エラー、関数が見つからない
          endif
        enddef

このエラーを回避するために exists_compiled() を使用できるが、その後で
"g:loaded_plugin" が定義されたとしても、この関数は呼び出されない:
        def CallPluginFunc()
          if exists_compiled('g:loaded_plugin')
            PluginFunc()   # 関数が呼ばれることはないかもしれない
          endif
        enddef

&opt = value はオプション "opt" に値を代入するようになったため、":&" を使用
して :substitute コマンドを繰り返すことはできない。
                                                        vim9-unpack-ignore
アンパック代入では、関数の引数を無視するのと同様に、アンダースコアを使用してリ
スト項目を無視できる:
        [a, _, c] = theList
残りの項目を無視するには:
        [a, b; _] = longList
                                                        E1163 E1080
アンパック記法を用いることで、1 度に複数の変数を宣言することが可能である。各変
数は型を持つか、値から型を推測することができる:
        var [v1: number, v2] = GetValues()
値を含むリストがある場合にのみこれを使用する。1 行に 1 つの変数を宣言すると、
読みやすく後で変更しやすくなる。


定数
                                                vim9-const vim9-final
定数の扱い方は言語によって異なる。ある言語では、別の値を代入できない変数を定数
とみす。JavaScript がその一例である。また、他の言語では値を不変とするため、定
数がリストを使用している場合は、リストを変更できない。Vim9 では、どちらも使用
できる。
                                                        E1021 E1307
:const は、変数と値の両方を定数にするために使用される。これは、変更されない
ことを確実にしたい複合構造に使用する。例:
        const myList = [1, 2]
        myList = [3, 4]         # エラー!
        myList[0] = 9           # エラー!
        myList->add(3)          # エラー!
                                                        :final E1125
:final は変数を定数にするために使用され、値は変更可能である。これは Java で
よく知られている。例:
        final myList = [1, 2]
        myList = [3, 4]         # エラー!
        myList[0] = 9           # OK
        myList->add(3)          # OK

定数は ALL_CAPS (すべて大文字) で書くのが一般的であるが、必須ではない。

この定数は値自体にのみ適用され、それが参照するものには適用されない。
        final females = ["Mary"]
        const NAMES = [["John", "Peter"], females]
        NAMES[0] = ["Jack"]     # エラー!
        NAMES[0][0] = "Jack"    # エラー!
        NAMES[1] = ["Emma"]     # エラー!
        NAMES[1][0] = "Emma"    # OK, females[0] == "Emma"


:call と :eval の省略
                                                        E1190
関数は :call なしで呼ぶことができる:
        writefile(lines, 'file')
:call の使用はまだ可能だが、推奨されない。

eval なしのメソッド呼び出しは、先頭が識別子であるか、Ex コマンドでない限り可
能である。関数の場合は、改行なしで "(" または "->" のいずれかが続く必要がある。
例:
        myList->add(123)
        g:myList->add(123)
        [1, 2, 3]->Process()
        {a: 1, b: 2}->Process()
        "foobar"->Process()
        ("foobar")->Process()
        'foobar'->Process()
        ('foobar')->Process()

関数名と Ex コマンドが曖昧になる稀なケースでは、Ex コマンドを使用することを明
確にするために、先頭に ":" を付ける。例えば、:substitute コマンドと
substitute() 関数の両方がある。行が substitute( で始まる場合は関数が使用さ
れる。代わりにコマンドを使用する場合は、先頭にコロンを付ける:
        :substitute(pattern (replacement (

式が "!" で始まる場合、これは条件の否定ではなくシェルコマンドとして解釈される。
つまり、これはシェルコマンドである:
        !shellCommand->something
否定を表す "!" を使用するには、式を括弧で囲む:
        (!expression)->Method()

Note 変数は使用する前に定義する必要があるが、関数は定義前に呼び出すことができ
る。これは、関数間の循環的な依存関係を可能にするために必要である。関数を名前で
検索する必要があるため、効率は少し悪くなる。また、関数名のタイプミスは、関数が
呼び出されたときにのみ検出される。


function() の省略

ユーザー定義関数は、function() を使わずに式の中で関数参照として使用できる。
その場合、引数の型と戻り値の型がチェックされる。関数は事前に定義されている必要
がある。

        var Funcref = MyFunction

function() を使用すると、結果の型は "func" になる。これは、任意の数の引数と
任意の戻り値 (void を含む) を持つ関数である。引数をクォートで囲めば、関数は後
で定義できる。


ラムダ式は -> の代わりに => を使う
                                                        vim9-lambda
旧来のスクリプトでは、メソッド呼び出しとラムダ式で "->" を使用することが混乱を
招く可能性がある。また、"{" が見つかった場合、パーサーはそれがラムダ式の開始な
のか辞書の開始なのかを判断する必要があるが、引数の型が使用されるようになったた
め、この判断はより複雑になっている。

これらの問題を回避するために、Vim9 script は JavaScript に似たラムダ式の異なる
構文を使用する:
        var Lambda = (arg) => expression
        var Lambda = (arg): type => expression
                                                        E1157
ラムダ式の引数では、"=>" まで改行は許可されない (Vim が括弧内の式とラムダ式の
引数を区別できるようにするため)。これは OK である:
        filter(list, (k, v) =>
                        v > 0)
これは動作しない:
        filter(list, (k, v)
                        => v > 0)
これも動作しない:
        filter(list, (k,
                        v) => v > 0)
ただし、パースする前にバックスラッシュを使用して行を連結することができる:
        filter(list, (k,
                \       v)
                \       => v > 0)
                                        vim9-lambda-arguments E1172
旧来のスクリプトでは、ラムダ式は任意の数の追加引数で呼び出すことができ、引数を
指定しなかった場合の警告を出す方法がなかった。Vim9 script では、引数の数は一致
している必要がある。任意の引数、あるいは追加の引数を受け入れたい場合は、"..._"
を使用する。これにより、関数は vim9-variable-arguments を受け入れるようにな
る。例:
        var Callback = (..._) => 'anything'
        echo Callback(1, 2, 3)  # "anything" を表示する

                                                inline-function E1171
さらに、ラムダ式には {} 内のステートメント群を含めることができる:
        var Lambda = (arg) => {
                g:was_called = 'yes'
                return expression
            }
これはタイマーに役立つ。例えば:
        var count = 0
        var timer = timer_start(500, (_) => {
                 count += 1
                 echom 'Handler called ' .. count
             }, {repeat: 3})

終わりの "}" は行頭になければならない。その後に他の文字を続けることもできる。
例:
        var d = mapnew(dict, (k, v): string => {
             return 'value'
           })
"{" の後にコマンドは使用できない。コメントのみ使用できる。

                                                command-block E1026
このブロックはユーザーコマンドの定義にも使用できる。ブロック内では Vim9 script
の構文が使用される。

以下はヒアドキュメントの使用例である:
    com SomeCommand {
        g:someVar =<< trim eval END
          ccc
          ddd
        END
      }

文に辞書が含まれる場合、その閉じ括弧を行頭に記述してはいけない。そうしないと、
ブロックの終わりとして解釈されてしまう。これは動作しない:
        command NewCommand {
             g:mydict = {
               'key': 'value',
               }  # エラー: ブロックの終了として認識される
           }
これを回避するには、最後の項目の後に '}' を配置する:
        command NewCommand {
             g:mydict = {
               'key': 'value' }
           }

理由: コマンドの後に "}" を置くことはできない。コマンドをパースして "}" を見つ
ける必要があるためである。一貫性を保つため、"{" の後にコマンドを置くことはでき
ない。残念ながら、これは "() => { command }" という表現が機能しないことを意味
する。常に改行が必要である。

                                                        vim9-curly
辞書リテラルの "{" がステートメントブロックとして認識されるのを避けるには、そ
れを括弧で囲む:
        var Lambda = (arg) => ({key: 42})

また、コマンドブロックの開始と混同された場合:
        ({
            key: value
         })->method()


自動行継続
                                        vim9-line-continuation E1097
多くの場合、式が次の行に続くことは明らかである。そのような場合、行頭にバックス
ラッシュ (line-continuation を参照) を付ける必要はない。例えば、リストが複数
行にまたがる場合:
        var mylist = [
                'one',
                'two',
                ]
辞書が複数行にまたがる場合:
        var mydict = {
                one: 1,
                two: 2,
                }
関数呼び出しの場合:
        var result = Func(
                        arg1,
                        arg2
                        )

[]、{}、() 以外の式における二項演算子については、演算子の直前または直後で改行
が可能である。例:
        var text = lead
                   .. middle
                   .. end
        var total = start +
                    end -
                    correction
        var result = positive
                        ? PosFunc(arg)
                        : NegFunc(arg)

"->" を使用するメソッド呼び出しとドットを使用するメンバーの場合、その前に改行
が許可される:
        var result = GetBuilder()
                        ->BuilderSetWidth(333)
                        ->BuilderSetHeight(777)
                        ->BuilderBuild()
        var result = MyDict
                        .member

コマンドリストを引数として持つコマンドの場合、行の先頭の | 文字は行の継続を示
す:
        autocmd BufNewFile *.match if condition
                |   echo 'match'
                | endif

Note これは、ヒアドキュメントの最初の行をバーで始めることができないことを意味
することに注意:
        var lines =<< trim END
           | これは動作しない
        END
先頭に空行を入れるか、ヒアドキュメントを使わないようにすること。あるいは、一時
的に 'cpoptions' に "C" フラグを追加する:
        set cpo+=C
        var lines =<< trim END
           | これは動作する
        END
        set cpo-=C
ヒアドキュメントが関数内にある場合、'cpoptions' は :def の前に設定し、:enddef
の後に復元する必要がある。

長い Ex コマンドを分割するなど、バックスラッシュによる行継続が必要な場所では、
コメントを '#\ ' で開始することができる:
        syn region Text
              \ start='foo'
              #\ コメント
              \ end='bar'
旧来のスクリプトと同様に、'"\ ' が使用される。これは、バックスラッシュなしで行
継続が使用され、行がバーで始まる場合にも必要である:
        au CursorHold * echom 'BEFORE bar'
              #\ 何かのコメント
              | echom 'AFTER bar'

                                                        E1050
行頭の演算子を認識できるようにするには、範囲の前にコロンを置く必要がある。次の
例では、"start" と "print" を追加する:
        var result = start
        + print
以下と同様:
        var result = start + print

これにより、"start" が割り当てられ、1 行表示する:
        var result = start
        :+ print

範囲指定の後には Ex コマンドが必要である。コロンがなくても :call なしで関数
を呼び出すことができるが、範囲指定の後には :call が必要である:
        MyFunc()
        :% call MyFunc()

Note +cmd 引数にはコロンは不要であることに注意:
        edit +6 fname

関数冒頭を引数間で複数行に分割することもできる:
        def MyFunc(
                text: string,
                separator = '-'
                ): string

継続行は容易に認識できないため、コマンドのパースはより厳密になった。例えば、1
行目のエラーのため、2 行目は別のコマンドとして認識される:
        popup_create(some invalid expression, {
           exit_cb: Func})
これで "exit_cb: Func})" は実際に有効なコマンドになった。つまり、"_cb: Func})"
ファイルへの変更を保存して終了する。Vim9 script でこのような間違いを避けるに
は、ほとんどのコマンド名と引数の間に空白を入れる必要がある。
E1144

ただし、コマンドの引数がコマンドである場合は認識されない。例えば、"windo echo
expr" の後では、"expr" 内の改行は認識されない。


Notes:
- "enddef" は継続行の先頭では使用できない。現在の関数を終了させるためである。
- 代入の左辺では改行は許可されない。特にリストを展開する場合に当てはまる
  :let-unpack。これは OK である:
        [var1, var2] =
                Func()
   これは動作しない:
        [var1,
            var2] =
                Func()
:echo:execute、および類似のコマンドの引数の間には改行を入れることはで
  きない。これは OK である:
        echo [1,
                2] [3,
                        4]
   これは動作しない:
        echo [1, 2]
                [3, 4]
- Vim では、コマンドのパースが困難な場合がある。特に、:windo のようにコマン
  ドが別のコマンドの引数として使用されている場合には、その傾向が顕著である。そ
  のような場合は、バックスラッシュによる行継続を使用する必要がある。


空白
                        E1004 E1068 E1069 E1074 E1127 E1202
Vim9 script では空白の適切な使用を強制する。これはもう許可されていない:
        var name=234    # エラー!
        var name= 234   # エラー!
        var name =234   # エラー!
"=" の前後には空白が必要である:
        var name = 234  # OK
コマンドの後ろのコメントを開始する # の前にも空白を入れる必要がある:
        var name = 234# エラー!
        var name = 234 # OK

ほとんどの演算子の周囲には空白が必要である。

サブリスト (リストスライス) では先頭と末尾を除き、":" の前後に空白が必要である:

        otherlist = mylist[v : count]   # v:count は異なる意味を持つ
        otherlist = mylist[:]           # リストのコピーを作る
        otherlist = mylist[v :]
        otherlist = mylist[: v]

空白は許可されない:
- 関数名と "(" の間:
        Func (arg)         # エラー!
        Func
             \ (arg)       # エラー!
        Func
              (arg)        # エラー!
        Func(arg)          # OK
        Func(
              arg)         # OK
        Func(
              arg          # OK
              )
                                                        E1205
:set コマンドではオプション名とそれに続く "&"、"!"、"<"、"="、"+="、"-=" ま
たは "^=" の間に空白は許可されていない。


波括弧の展開の禁止

波括弧変数 curly-braces-names は使用できない。


コマンド修飾子は無視されない
                                                                E1176
コマンド修飾子を使用しないコマンドにコマンド修飾子を使用するとエラーが発生す
る。
                                                                E1082
また、後続のコマンドなしでコマンド修飾子を使用するとエラーになるようになった。


辞書リテラル
                                                vim9-literal-dict E1014
従来、Vim は {} 構文を使用して辞書リテラルをサポートしてきた:
        let dict = {'key': value}

その後、単純なテキストのキーの使用が非常に一般的であることが明らかになったた
め、後方互換性のある形でリテラル辞書が導入された:
        let dict = #{key: value}

しかし、この #{} 構文は既存の言語とは異なる。リテラルキーの使用は式の使用より
もはるかに一般的であり、JavaScript でもこの構文が使用されていることを考慮する
と、辞書リテラルに {} 形式を使用する方がはるかに便利な構文であると考えられる。
Vim9 script では、{} 形式はリテラルキーを使用する:
        var dict = {key: value}

これは英数字、アンダースコアおよびダッシュに有効である。他の文字を使用する場合
は、シングルクォートまたはダブルクォートで囲んだ文字列を使用する:
        var dict = {'key with space': value}
        var dict = {"key\twith\ttabs": value}
        var dict = {'': value}                  # 空のキー
                                                        E1139
キーに式を使用する必要がある場合は、JavaScript と同様に角括弧を使用できる:
        var dict = {["key" .. nr]: value}

キーの型は文字列、数値、真偽値、または浮動小数点数である。その他の型はエラーに
なる。[] を使用しない場合、値は先頭のゼロを保持した文字列として扱われる。[] で
指定された式は評価され文字列に変換される。その際、先頭のゼロは削除される:
        var dict = {000123: 'without', [000456]: 'with'}
        echo dict
        {'456': 'with', '000123': 'without'}
ドットは他の場所では受け入れられないため、浮動小数点数は [] 内でのみ動作する:
        var dict = {[00.013]: 'float'}
        echo dict
        {'0.013': 'float'}


:xit、:t、:k、:append、:change、:insert の禁止
                                                        E1100
これらのコマンドは、ローカル変数名と混同されやすい。
:x や :xit の代わりに :exit を使用できる。
:t の代わりに :copy を使用できる。
:k の代わりに :mark を使用できる。


比較演算子

'ignorecase' オプションは、文字列を使用する比較演算子では使用されない。
したがって、"=~" は "=~#" のように動作する。

文字列に対して "is" と "isnot" (expr-is と expr-isnot) を使用すると、偽を
返すようになった。旧来のスクリプトでは文字列の比較のみだったが、Vim9 script
では文字列の同一性を確認し、使用時に文字列がコピーされるため、2 つの文字列が同
一になることはない (文字列がコピーされず参照カウントされるようになれば、将来的
には変更される可能性がある)。


エラー後の中断

旧来のスクリプトではエラーが発生すると、Vim は次の行の実行を継続する。これによ
り、長いエラーシーケンスが発生し停止するには Ctrl-C を入力する必要がある。Vim9
script では、コマンドの実行は最初のエラーで停止する。例:
        vim9script
        var x = does-not-exist
        echo 'not executed'


For ループ
                                                        E1254
ループ変数は宣言されていてはいけない:
        var i = 1
        for i in [1, 2, 3]   # エラー!

ただし、グローバル変数を使用することは可能である:
        g:i = 1
        for g:i in [1, 2, 3]
          echo g:i
        endfor

旧来の Vim script には、リストハンドルに対してforループを実行し、現在位置また
は前の項目を削除するトリックがいくつかある。Vim9 script ではインデックスのみを
使用し、項目が削除された場合はリスト内の項目をスキップする。旧来のスクリプトの
例:
        let l = [1, 2, 3, 4]
        for i in l
           echo i
           call remove(l, index(l, i))
        endfor
以下が出力される:
        1
        2
        3
        4
コンパイルされた Vim9 script では以下のようになる:
        1
        3
一般的に、反復処理するリストは変更してはならない。必要であれば事前にコピーを作
成する。
リストのリストをループ処理する場合、ネストされたリストは変更できる。ループ変数
は "final" であり変更できないが、その値は変更できる。
                                                        E1306
:for ループと :while ループを合わせたループの深さは、10 を超えることはできな
い。


条件と式
                                                vim9-boolean
条件と式は他の言語とほぼ同じように動作する。ただし、一部の値は旧来の Vim scipt
と異なる:
        値              旧来の Vim script       Vim9 script
        0               falsy                   falsy
        1               truthy                  truthy
        99              truthy                  エラー!
        "0"             falsy                   エラー!
        "99"            truthy                  エラー!
        "text"          falsy                   エラー!

"??" 演算子や "!" 演算子を使用する場合エラーは発生せず、すべての値は偽値か真値
のいずれかになる。これは JavaScript とほぼ同じだが、空のリストと辞書は偽値にな
る:

        型              真値となるとき
        bool            true, v:true または 1
        number          非0
        float           非0
        string          空以外
        blob            空以外
        list            空以外 (JavaScript とは異なる)
        tuple           空以外 (JavaScript とは異なる)
        dictionary      空以外 (JavaScript とは異なる)
        func            関数名があるとき
        special         true または v:true
        job             非 NULL
        channel         非 NULL
        class           非 NULL
        object          非 NULL (TODO: isTrue() が true を返すとき)

真偽値演算子 "||" と "&&" は、値が真偽値、0 または 1 であることを期待する:
        1 || false   == true
        0 || 1       == true
        0 || false   == false
        1 && true    == true
        0 && 1       == false
        8 || 0       エラー!
        'yes' && 0   エラー!
        [] || 99     エラー!

論理否定に "!" を使用する場合、どの型を使用してもエラーは発生せず、結果は真偽
値になる。"!!" を使用すると、任意の値を真偽値に変換できる:
        !'yes'                  == false
        !![]                    == false
        !![1, 2, 3]             == true

文字列の連結に .. を使用する場合、単純型の引数は常に文字列に変換される:
        'hello ' .. 123  == 'hello 123'
        'hello ' .. v:true  == 'hello true'

単純型は Number、Float、Special、Bool である。その他の型には string() を使用
すべきである。
                        false true null null_blob null_channel
                        null_class null_dict null_function null_job
                        null_list null_object null_partial null_string
                        E1034
Vim9 script では以下の定義済みの値が使用できる:
        true
        false
        null
        null_blob
        null_channel
        null_class
        null_dict
        null_function
        null_job
        null_list
        null_tuple
        null_object
        null_partial
        null_string
true は v:true と同じ、false は v:false と同じ、null は v:null と
同じである。

null は "special" 型だが、その他の "null_" で始まる値は名前で示された型にな
る。多くの場合、null 値は空の値と同じように扱われるが、必ずしもそうとは限らな
い。これらの値はスクリプトローカル変数をクリアするのに便利である。なぜなら
:unlet では削除できないためである。例:
        var theJob = job_start(...)
        # ジョブに仕事をさせる
        theJob = null_job

これらの値は引数のデフォルト値としても役立つ:
        def MyFunc(b: blob = null_blob)
            # Note: null_blob ではなく null と比較し、
            #       デフォルト値と空 blob を区別する。
            if b == null
                # 引数 b が与えられなかった
null との比較テストに関する詳細は、null-compare を参照。

null を任意の値と比較することは可能であり、型エラーは発生しない。ただし、
null を数値、浮動小数点数、または真偽値と比較すると、常に false が返され
る。これは、null を 0 または false と比較すると true が返される旧来のス
クリプトとは異なる。
                                                        vim9-false-true
真偽値を文字列に変換する場合、旧来のスクリプトのように v:false と v:true
ではなく、 false と true が使用される。v:none には none の代替がなく、
他の言語にも同等のものはない。
                                                        vim9-string-index
[idx] による文字列のインデックス指定、または [idx : idx] によるスライスの取得
では、バイトインデックスではなく文字インデックスが使用される。合成文字も含まれ
る。例:
        echo 'bár'[1]
旧来のスクリプトでは文字 0xc3 (不正なバイト) が返され、Vim9 script では文字列
'á' が返される。
負のインデックスは末尾からカウントし、"[-1]" は最後の文字である。
最後の文字を除外するには slice() を使用する。
合成文字を個別にカウントするには strcharpart() を使用する。
インデックスが範囲外の場合、結果は空文字列になる。

旧来のスクリプトでは、"++var" と "--var" は何も表示されずにそのまま受け入れら
れ効果がない。これは Vim9 script ではのエラーである。

0 で始まる数値は 8 進数とはみなされず、"0o" で始まる数値のみが 8 進数として扱
われる: "0o744"。scriptversion-4


気をつけるべきこと
                                                        vim9-gotchas
Vim9 は、一般的なプログラミング言語に近づくように設計されていますが、同時に旧
来の Vim コマンドをサポートしようとしています。そのため、いくつかの妥協をしな
ければなりませんでした。ここでは、意外と知られていないことをまとめてみました。

Exコマンドの範囲指定にはコロンを前置する必要があります。
        ->                旧来の Vim: 前の行を右にシフト
        ->func()          Vim9: 継続行におけるメソッド呼び出し
        :->               Vim9: 前の行を右にシフト

        %s/a/b            旧来の Vim: すべての行を置換
        x = alongname
             % another    Vim9: 継続行の剰余演算
        :%s/a/b           Vim9: すべての行を置換
        't                旧来の Vim: マーク t へのジャンプ
        'text'->func()    Vim9: メソッド呼び出し
        :'t               Vim9: マーク t へのジャンプ

いくつかのExコマンドは Vim9 script の代入式と紛らわしくなります:
        g:name = value    # 代入
        :g:pattern:cmd    # :グローバルコマンド

コマンド :global や :substitute と式や代入文が紛らわしくなるのを避けるた
め、これらのコマンドが一文字に省略されているとき、一部のセパレータは使うことが
できません: ':'、'-' と '.' が利用不可です。:
        g:pattern:cmd     # 無効なコマンド - エラー
        s:pattern:repl    # 無効なコマンド - エラー
        g-pattern-cmd     # 無効なコマンド - エラー
        s-pattern-repl    # 無効なコマンド - エラー
        g.pattern.cmd     # 無効なコマンド - エラー
        s.pattern.repl    # 無効なコマンド - エラー

同様に、コマンドとセパレータの間にスペースがあってはいけません:
        g /pattern/cmd    # 無効なコマンド - エラー
        s /pattern/repl   # 無効なコマンド - エラー

:def で定義した関数はすべてコンパイルされます。旧来の関数は途中で脱出するこ
とができ、それ以降の行はパースされません:
        func Maybe()
          if !has('feature')
            return
          endif
          use-feature
        endfunc
Vim9 関数はすべてコンパイルされます:
        def Maybe()
          if !has('feature')
            return
          endif
          use-feature  # コンパイルエラーが発生する可能性がある
        enddef
応急的に、2つの関数に分けることができます:
        func Maybe()
          if has('feature')
            call MaybeInner()
          endif
        endfunc
        if has('feature')
          def MaybeInner()
            use-feature
          enddef
        endif
また、偽として評価される定数式の条件をもった if の配下にサポート外のコードを
置くことができます:
        def Maybe()
          if has('feature')
            use-feature
          endif
        enddef
これには exists_compiled() 関数も同様に使えます。
                                                        vim9-user-command
関数のコンパイルによる他の副作用として、ユーザーコマンドの存在がコンパイルの時
点でチェックされます。ユーザーコマンドが後で定義されている場合、エラーとなりま
す。これはOKです:
        command -nargs=1 MyCommand echom <q-args>
        def Works()
          MyCommand 123
        enddef
これは "MyCommand" が定義されていないというエラーが発生します:
        def Works()
          command -nargs=1 MyCommand echom <q-args>
          MyCommand 123
        enddef
回避策は、:execute を使用して間接的にコマンドを呼び出すことです:
        def Works()
          command -nargs=1 MyCommand echom <q-args>
          execute 'MyCommand 123'
        enddef

Note 認識されていないコマンドを "|" でつなぐと、その後のコマンドは認識されませ
ん。次のような記述は endif がないというエラーになります:
        def Maybe()
          if has('feature') | use-feature | endif
        enddef

その他の変更点

パターンは、明示的に上書きされない限り 'magic' が設定されている状態と同様に作
用します。
オプション 'edcompatible' の値は使用されません。
オプション 'gdefault' の値は使用されません。

また、このwikiも参考になるでしょう。これは Vim9 script のアーリーアダプターに
よって書かれました: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md

                                                        :++ :--
++ と -- コマンドが追加されました。1 を足し引きするのとそっくりです:
                ++var
                var += 1
                --var
                var -= 1

式中で ++var や --var を使うことはまだサポートされていません。

==============================================================================

3. 新しいスタイルの関数                                 fast-functions

                                                        :def E1028
:def[!] {name}([arguments])[: {return-type}]
                        {name} という名前の新しい関数を定義します。関数の本体
                        は次の行から :enddef と一致するまで続きます。
                        E1073
                                                        E1011
                        {name} は必ず 100 バイト未満でなければなりません。
                                        E1003 E1027 E1056 E1059
                        :return で返される値の型は必ず {return-type} と一致
                        しなければなりません。{return-type} が省略された、ある
                        いは "void" である場合は、その関数は何も返さないと想定
                        されます。
                                                        E1077 E1123
                        {arguments} は 0 あるいはそれ以上の引数の宣言列です。
                        引数の宣言には 3 つの書式があります:
                                {name}{type}
                                {name} = {value}
                                {name}{type} = {value}
                        最初の書式は必須の引数の書式で、呼び出す側は必ず実引数
                        を与える必要があります。
                        2 つ目と 3 つ目の書式は任意の引数の書式です。呼び出す
                        側が実引数を省略した場合は、{value} が使われます。

                        関数は呼び出された時、:disassemble が使われたとき、
                        あるいは :defcompile が使われたときに命令列にコンパ
                        イルされます。文法および型のエラーはこの時に提示されま
                        す。

                        :def を他の :def や :function の内側で大体 50 階
                        層の深さまでまでネストすることが可能です。
                                                        E1117
                        [!] は :function と同様に使われます。Note Vim9
                        script において、スクリプトローカル関数は後で削除され
                        たり再定義されたりしてはいけません。スクリプトローカル
                        の削除は、同じスクリプトの再読み込みによってのみ行えま
                        す。

                                        :enddef E1057 E1152 E1173
:enddef                 :def で定義された関数の終了。:enddef はそれだけで
                        行にあるべきです。

また、このwikiも参考になるでしょう。これは Vim9 script のアーリーアダプターに
よって書かれました: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md

もし関数が定義されたスクリプトが Vim9 script であるなら、スクリプトローカル変
数はプリフィックス "s:" なしでアクセスすることができます。それらは関数がコンパ
イルされる前に定義されている必要があります。もし関数が定義されたスクリプトが旧
来の Vim script であるなら、スクリプトローカル変数は、コンパイル時に存在しない
場合は、プリフィックス "s:" をつけてアクセスする必要があります。
                                                        E1269
Vim9 script では、スクリプトローカル変数はスクリプトレベルで宣言されなければ
なりません。それらは関数内で、旧来の関数内でも作成することはできません。

                                                :defc :defcompile
:defc[ompile]           現在のスクリプトで定義されている関数とクラス
                        (class-compile) のうち、まだコンパイルされていないも
                        のをコンパイルします。これはコンパイル時に見つかったい
                        かなるエラーも報告します。

:defc[ompile] MyClass   クラス内のすべてのメソッドをコンパイルします。
                        class-compile

:defc[ompile] {func}
:defc[ompile] debug {func}
:defc[ompile] profile {func}
                        必要であれば関数 {func} をコンパイルします。"debug" と
                        "profile" はコンパイルモードを指定するのに使います。こ
                        れはコンパイル時に見つかったいかなるエラーも報告しま
                        す。
                        {func} コールは、"ClassName.functionName" とすること
                        で、クラス内の関数やメソッドもコンパイルできます。
                        {func} コールは、"ClassName" とすることで、クラス内の
                        すべての関数とメソッドをコンパイルすることもできます。

                                                :disa :disassemble
:disa[ssemble] {func}   {func} 用に生成された命令列を表示します。これはデバ
                        ッグ及びテスト用です。 E1061
                        Note {func} のコマンドライン補完において、スクリプト
                        ローカル関数を見つけるのに "s:" を前置することができ
                        ます。

:disa[ssemble] profile {func}
                        :disassemble と似ていますが、プロファイルをとるとき
                        に使われる命令列を表示します。

:disa[ssemble] debug {func}
                        :disassemble と似ていますが、デバッグをするときに使
                        われる命令列を表示します。

制約

ローカル変数は文字列式からは見えません。例:
        def MapList(): list<string>
          var list = ['aa', 'bb', 'cc', 'dd']
          return range(1, 2)->map('list[v:val]')
        enddef

この map の引数は関数のスコープ抜きに評価される文字列式です。代わりにラムダ式
を使ってください:
        def MapList(): list<string>
          var list = ['aa', 'bb', 'cc', 'dd']
          return range(1, 2)->map((_, v) => list[v])
        enddef

:edit のような、コンパイルされないコマンドには、バッククォートによる展開が
使え、またそれはローカルスコープを使うことができます。例:
        def Replace()
          var fname = 'blah.txt'
          edit `=fname`
        enddef

ループ内で定義されたクロージャは同じコンテキストを共有します。例:
        var flist: list<func>
        for i in range(5)
          var inloop = i
          flist[i] = () => inloop
        endfor
        echo range(5)->map((i, _) => flist[i]())
        # 結果: [4, 4, 4, 4, 4]
                                                        E1271
クロージャはそのコンテキスト内の変数を見つけられるように、必ずそれが定義された
コンテキストでコンパイルされます。これは関数がコンパイルされた後に :breakadd
で関数がデバッグ対象だとマークされたときを除いて、これは大体正しく行われます。
必ず外側の関数がコンパイルされるより前にブレークポイントを定義するように気をつ
けてください。

「ループ中」の変数は一度しか存在しません。リストに入れた全てのクロージャは同じ
インスタンス、すなわち最後に 4 の値をもつインスタンスを参照します。これは効率
的で、何回もループする場合でも同様です。もしそれぞれのクロージャでコンテキスト
を分けたいのであれば、コンテキストを定義するため関数を呼んでください:
        def GetClosure(i: number): func
          var infunc = i
          return () => infunc
        enddef

        var flist: list<func>
        for i in range(5)
          flist[i] = GetClosure(i)
        endfor
        echo range(5)->map((i, _) => flist[i]())
        # 結果: [0, 1, 2, 3, 4]

いくらかの場面で、特に旧来の Vim script のコンテキストから Vim9 のクロージャを
呼ぶとき、その評価は失敗するでしょう。 E1248

Note スクリプトレベルにおいて、ループ変数はループの後では無効になります。これ
はループ変数が後で呼ばれるクロージャで使われている場合、例えばタイマーと組み合
わせる場合でも同様です。これは E1302 エラーを発生させます:
        for n in range(4)
            timer_start(500 * n, (_) => {
                  echowin n
               })
        endfor

ブロックを使って変数を定義し、その変数をクロージャで使う必要があります:
        for n in range(4)
        {
           var nr = n
           timer_start(500 * n, (_) => {
                  echowin nr
              })
        }
        endfor

タイマーにおいて :echowindow を使うのは便利です。メッセージはポップアップに
表示され、タイマーがトリガーされたときにユーザーが行っていることに干渉しませ
ん。


旧来の関数から Vim9 の関数への変換
                                        convert_legacy_function_to_vim9
これらが旧来の関数から Vim9 の関数へ変換するために行われる必要のある変更の大部
分です。

func や function を def に変更する。
endfunc や endfunction を enddef に変更する。
- 関数の引数に型をつける。
- もし関数が何か返すのであれば、戻り値の型をつける。
- コメントが " に代わって # で始まるように変更する。

  例えば旧来の Vim script の関数:
        func MyFunc(text)
          " 関数の本体
        endfunc
   がこうなる:
        def MyFunc(text: string): number
          # 関数の本体
        enddef

- 引数に使われる "a:" を削除する。例:
        return len(a:text)
   がこうなる:
        return len(text)

- 変数の宣言に使われる let を var に変更する。
- 変数への値の代入に使われる let を削除する。これは既に宣言されているローカ
  ル変数と b: w: g: t: 変数が対象である。

  例えば旧来の Vim script の関数:
          let lnum = 1
          let lnum += 3
          let b:result = 42
   がこうなる:
          var lnum = 1
          lnum += 3
          b:result = 42

- 式中の必要なところへホワイトスペースを挿入する。
- 結合に使われる "." を ".." に変更する。

  例えば旧来の Vim script の関数:
          echo line(1).line(2)
   がこうなる:
          echo line(1) .. line(2)

- 常に行継続にバックスラッシュが必要なわけではない:
                echo ['one',
                \ 'two',
                \ 'three'
                \ ]
   がこうなる:
        echo ['one',
                'two',
                'three'
                ]


式のオプションで関数を呼び出す
                                                        expr-option-function
'foldexpr' などのいくつかのオプションの値は、値を取得するために評価される式で
す。評価には、かなりのオーバーヘッドが発生する可能性があります。オーバーヘッド
を最小限に抑え、オプション値を非常に単純に保つ 1 つの方法は、コンパイル済み関
数を定義し、引数なしで呼び出すようにオプションを設定することです。例:
        vim9script
        def MyFoldFunc(): any
           ... v:lnum の行の折り畳みレベルを計算する
           return level
        enddef
        set foldexpr=s:MyFoldFunc()

==============================================================================

4. 型                                   vim9-types
                                        E1008 E1009 E1010 E1012
                                        E1013 E1029 E1030
以下の組み込み型がサポートされています:
        bool
        number
        float
        string
        blob
        list<{type}>
        dict<{type}>
        object<{type}>
        job
        channel
        tuple<{type}>
        tuple<{type}{type}, ...>
        tuple<...list<{type}>>
        tuple<{type}, ...list<{type}>>
        func
        func: {type}
        func({type}, ...)
        func({type}, ...): {type}
        void

これらの型は宣言において使えますが、いかなる単純値も実際に "void" 型を持つこと
はありません。void (例えば、戻り値のない関数) を使おうとするとエラーがでます。
E1031  E1186

配列型はありません。代わりに list<{type}> を使ってください。不変のリストに
対しては大量の細かいメモリを割り当てするのを避ける効率的な実装が使われます。
                                                        tuple-type
tuple 型は、多かれ少なかれ具体的な方法で宣言できる:
tuple<number>                   Number 型の項目を 1 つ含む tuple
tuple<number, string>           Number と String の 2 つの項目を含む tuple
tuple<number, float, bool>      NumberFloat および Boolean 型の 3 つの
                                項目を含む tuple
tuple<...list<number>>          Number 型の 0 個以上の項目を持つ可変長 tuple
tuple<number, ...list<string>>  Number 型の項目とそれに続く 0 個以上の
                                String 型の項目を含む tuple

例:
    var myTuple: tuple<number> = (20,)
    var myTuple: tuple<number, string> = (30, 'vim')
    var myTuple: tuple<number, float, bool> = (40, 1.1, true)
    var myTuple: tuple<...list<string>> = ('a', 'b', 'c')
    var myTuple: tuple<number, ...list<string>> = (3, 'a', 'b', 'c')

                                                variadic-tuple E1539
可変長 tuple には、同じ型の 0 個以上の項目が含まれる。可変長 tuple の型はリス
ト型で終わる必要がある。例:
    var myTuple: tuple<...list<number>> = (1, 2, 3)
    var myTuple: tuple<...list<string>> = ('a', 'b', 'c')
    var myTuple: tuple<...list<bool>> = ()

                                    vim9-func-declaration E1005 E1007
部分適用と関数は幾らかの方法で宣言することができます:
func                            任意の種類の関数参照。引数や戻り値への型チェッ
                                クはない。
func: void                      任意の数および型の引数で、戻り値はない。
func: {type}                    任意の数および型の引数で、特定の型の戻り値があ
                                る。

func()                          引数がなく、値を返さない関数。
func(): void                    同上
func(): {type}                  引数がなく、戻り値の型がある関数。

func({type})                    引数の型があり、値を返さない関数。
func({type}): {type}            引数の型と戻り値の型がある関数。
func(?{type})                   任意の引数の型があり、値を返さない関数。
func(...list<{type}>)           可変長引数のリストの型で、値を返さない関数。
func({type}, ?{type}, ...list<{type}>): {type}
                                以下をもつ関数:
                                - 必須の引数の型
                                - 任意の引数の型
                                - 可変長引数のリストの型
                                - 戻り値の型

もし戻り値の型が "void" なら、関数は値を返しません。

関数参照はそれが呼び出し側から見えない追加の引数および・あるいは辞書を保存して
いる場合、Partial にすることもできます。それらは同じように呼び出されるた
め、宣言も同じです。

:type を使ってカスタム型を定義できます:
        :type MyList list<string>
ユーザー関数と似たように、後から追加される組み込み型との名前の衝突を避けるた
め、カスタム型は大文字から始まらなければなりません。

そしてクラスとインターフェイスも型として使えます:
        :class MyClass
        :var mine: MyClass

        :interface MyInterface
        :var mine: MyInterface

        :class MyTemplate<Targ>
        :var mine: MyTemplate<number>
        :var mine: MyTemplate<string>

        :class MyInterface<Targ>
        :var mine: MyInterface<number>
        :var mine: MyInterface<string>
{not implemented yet}


変数の型と型キャスト
                                                        variable-types
Vim9 script か :def で定義される関数内で宣言された変数は明示的に示された型
か、初期値から推測された型のどちらかの型を持っています。

グローバル、バッファ、ウィンドウ、タブページ変数は特定の型を持たず、値はいつで
も書き換えられ、そしてそれは型の変更も含み得ます。なので、コンパイルされたコー
ドでは "any" 型が仮定されます。

これは "any" 型が望ましくなく、実際の型が常に同じであると想定されるときに問題
になり得ます。例えば、リストを宣言したとき:
        var l: list<number> = [1, g:two]
コンパイル時には Vim は "g:two" の型を知らず、式の型は list<any> になります。
左辺への代入の前にリストの型をチェックするための命令が生成され、少々非効率で
す。
                                                type-casting E1104
これを避けるには、型キャストを使ってください:
        var l: list<number> = [1, <number>g:two]
コンパイルされたコードは今度は "g:two" が数値かどうかをチェックするだけで、も
しそうでないならエラーを与えます。これは型キャストと呼ばれます。

型キャストの文法は: "<" {type} ">" です。"<" の後ろ、あるいは ">" の前にホワイ
トスペースがあってはいけません (小なりと大なりの演算子との混乱を避けるためで
す)。

意味としては、必要であれば実行時の型チェックが行われます。実際に値が変更される
ことはありません。もし型を変える必要があるのであれば、例えば文字列に変換するの
であれば string() 関数を使ってください。あるいは文字列を数値に変換するのであ
れば str2nr() 関数を使ってください。

もし想定されない場所で型が与えられた場合、 E1272 を得るかもしれません。

型が不完全な場合、例えば、クラスが不明なオブジェクト (通常は NULL オブジェクト)
がある場合などは E1363 になります。

型インターフェイス
                                                        type-inference
一般的に: 型が明確な時はいつも型を省略することができます。例えば、変数を宣言
し、値を与えた時:
        var name = 0            # 数値型と推測する
        var name = 'hello'      # 文字列型と推測する

リストと辞書の型は、要素の値の型の共通のものからきます。もしその値が全て同じ型
をもつなら、その型がリストか辞書に使われます。もし型が混在しているなら、"any"
型が使われます。
        [1, 2, 3]       list<number>
        ['a', 'b', 'c'] list<string>
        [1, 'x', 3]     list<any>

関数参照の共通の型は、もしそれらが全て同じ数の引数をもつのでなければ、引数の
数が指定されていないことを示すため "(...)" を使います。例:
        def Foo(x: bool)
        enddef
        def Bar(x: bool, y: bool)
        enddef
        var funclist = [Foo, Bar]
        echo funclist->typename()
結果はこうなります:
        list<func(...)>

Vim9 script のスクリプトローカル変数は型がチェックされ、それは変数が旧来の Vim
script の関数内で宣言されたときでも同様です。

型が宣言されたとき、これはリストや辞書に付け加えられます。後のある式がその型を
変えようとすると、エラーが与えられます:
        var ll: list<number> = [1, 2, 3]
        ll->extend(['x'])  # エラー、'x' は数値ではない

もし型が宣言されていないのであれば、そのときは変えることは許可されます:
        [1, 2, 3]->extend(['x'])  # 結果: [1, 2, 3, 'x']

変数の宣言においては、推測された型は重要です:
        var ll = [1, 2, 3]
        ll->extend(['x'])  # エラー、'x' は数値ではない
これは宣言が数値のリストのように見えるためで、それゆえ以下と等しいからです:
        var ll: list<number> = [1, 2, 3]
もし、もっと寛容なリストが欲しいのであれば、型を宣言する必要があります:
        var ll: list<any> = [1, 2, 3]
        ll->extend(['x'])  # OK


より厳格な型チェック
                                                        type-checking
旧来の Vim script では、数値が予期されるところでは、文字列は自動的に数値に変換
されます。これは "123" のような実際の数値に対しては便利でしたが、もし文字列が
数字で始まらないときは予期しない問題 (加えてエラーメッセージなしで) を引き起こ
します。これは頻繁に見つけにくいバグを引き起こします。例:
        echo 123 == '123'
        1
思いがけないスペースがあるとき:
        echo 123 == ' 123'
        0
                                                        E1206 E1210 E1212
Vim9 script ではこれは厳格にされています。使われている値が予期される型と一致す
る場合は、ほとんどの場所で前と同じように動作します。時々エラーになり、それゆえ
後方互換性が壊れています。例:
- 真偽値が期待されるところで、値が 0 か 1 でない数値を使う。  E1023
- 数値のオプションを設定するときに文字列を使う。   E1024 E1105
- 文字列が期待されるところで数値を用いる。

一つの影響として、型が宣言された場合は map() に渡されたリストか辞書の要素の
型は変更されてはいけません。これは Vim9 script ではエラーになります:
        var mylist: list<number> = [1, 2, 3]
        echo map(mylist, (i, v) => 'item ' .. i)
        E1012: Type mismatch; expected number but got string in map()
        {訳注: 日本語メッセージの場合: "E1012: 型が不一致です。number が必要で
        すが string でした (map() 内)"}

代わりに、新しくリストを作成する mapnew() を使ってください:
        var mylist: list<number> = [1, 2, 3]
        echo mapnew(mylist, (i, v) => 'item ' .. i)
        ['item 0', 'item 1', 'item 2']

もし要素の型が宣言されていない、あるいは "any" と決定されているなら、型はより
具体的なものに変更することができます。例えば、型の混ざったリストが文字列のリス
トに変更される場合:
        var mylist = [1, 2.0, '3']
        # typename(mylist) == "list<any>"
        map(mylist, (i, v) => 'item ' .. i)
        # typename(mylist) == "list<string>"、エラーなし

リスト定数を直接使うのと、変数宣言を通して使うのには少しの差異があります。
理由は型推論で、リスト定数を変数を初期化するのに使うとき、それは同時に宣言した
型をつけます:
        var mylist = [1, 2, 3]
        # typename(mylist) == "list<number>"
        echo map(mylist, (i, v) => 'item ' .. i)  # エラー!

リスト定数を直接使うときは、型は宣言されず、変更することが許可されます:
        echo map([1, 2, 3], (i, v) => 'item ' .. i)  # OK

この背景となる理由は、型が宣言され、リストが渡されて変更された場合、宣言は常に
保持されている必要があるためです。そのため、宣言された型と一致する型に依存して
います。定数の場合、これは必要ありません。

                                                                E1158
extend() も同様で、代わりに extendnew() を、flatten() は、代わりに
flattennew() を使用します。flatten() は常に型を変更することを目的としてい
るため、Vim9 script では使用できません。

引数を指定して funcref に代入すると (vim9-func-declaration を参照)、引数の厳
密な型チェックが行われます。可変数の引数の場合も、型と一致する必要があります:

        var FuncRef: func(string, number, bool): number
        FuncRef = (v1: string, v2: number, v3: bool) => 777     # OK
        FuncRef = (v1: string, v2: number, v3: number) => 777   # エラー!
        # 可変数の引数は同じ型でなければならない
        var FuncVA: func(...list<string>): number
        FuncVA = (...v: list<number>): number => v  # エラー!
        FuncVA = (...v: list<any>): number => v     # OK, `any` ランタイム確認
        FuncVA = (v1: string, v: string2): number => 333     # エラー!
        FuncVA = (v: list<string>): number => 3     # エラー!

宛先関数参照に引数が指定されていない場合、引数の型のチェックは行われません:
        var FuncUnknownArgs: func: number
        FuncUnknownArgs = (v): number => v                      # OK
        FuncUnknownArgs = (v1: string, v2: string): number => 3 # OK
        FuncUnknownArgs = (...v1: list<string>): number => 333  # OK

                         E1211 E1217 E1218 E1219 E1220 E1221
                         E1222 E1223 E1224 E1225 E1226 E1227
                         E1228 E1235 E1238 E1250 E1251 E1252
                         E1253 E1256 E1297 E1298 E1301 E1528
                         E1529 E1530 E1531 E1534
間違いを発見しやすくするために、ほとんどの組み込み関数で型がチェックされます。

変数のカテゴリー、デフォルトおよび null の扱い
                                variable-categories null-variables
変数には次のカテゴリがあります:
        プリミティブ    number, float, boolean
        コンテナ        string, blob, list, tuple, dict
        特殊            function, job, channel, user-defined-object

初期化子を使用せずに変数を宣言する場合は、明示的に型を指定する必要があります。
各カテゴリには、異なるデフォルトの初期化セマンティクスがあります。カテゴリごと
の例を次に示します:
        var num: number         # プリミティブのデフォルトは 0 相当
        var cont: list<string>  # コンテナのデフォルトは空コンテナ
        var spec: job           # 特殊変数のデフォルトは null

Vim にはおなじみの null 値はありません。null_stringnull_listnull_job
など、さまざまな null_<type> があらかじめ定義されています。プリミティブは
null_<type> を持ちません。null_<type> の典型的な使用例は以下の通りです:
- 変数をクリアし、そのリソースを解放。
- 関数定義内のパラメータのデフォルトとしては、null-compare を参照。

job などの特殊な変数の場合、リソースをクリアするために null_<type> が使用さ
れます。コンテナ変数の場合、空コンテナを変数に割り当てることによってリソースを
クリアすることもできます。例:
        var j: job = job_start(...)
        # ... ジョブはその仕事を行う
        j = null_job    # 変数をクリアしてジョブのリソースを解放する

        var l: list<any>
        # ... リストにたくさんのものを追加
        l = []  # 変数をクリアしてコンテナリソースを解放する
null_<type> ではなく空コンテナを使用してコンテナ変数をクリアすると、
null-anomalies で説明されているように null の複雑な問題を回避できる可能性が
あります。

コンテナ変数と特殊な変数の初期化セマンティクスは異なります。初期化されていない
コンテナはデフォルトで空コンテナになります:
        var l1: list<string>                # 空コンテナ
        var l2: list<string> = []           # 空コンテナ
        var l3: list<string> = null_list    # null コンテナ
"l1" と "l2" は同等で区別できない初期化です。ただし、"l3" は null コンテナで
す。null コンテナは空コンテナに似ていますが、異なります。null-anomalies を参
照してください。

特殊変数のデフォルトは null です。これらのジョブの初期化は同等であり、区別でき
ません:
        var j1: job
        var j2: job = null_job
        var j3 = null_job

リストまたは辞書が宣言されている時に、項目の型が指定されておらず推論できない場
合、型は "any" になります:
        var d1 = {}             # 型は "dict<any>"
        var d2 = null_dict      # 型は "dict<any>"

関数の宣言は特に独特です。vim9-func-declaration を参照。

                                                null-compare
一般的な null 比較セマンティクスでは、null コンテナが空コンテナと等しくない場
合、比較で null_<type> を使用しないでください:
        vim9script
        def F(arg: list<string> = null_list)
            if arg == null
               echo "null"
            else
                echo printf("not null, %sempty", empty(arg) ? '' : 'not ')
            endif
        enddef
        F()             # 出力: "null"
        F(null_list)    # 出力: "null"
        F([])           # 出力: "not null, empty"
        F([''])         # 出力: "not null, not empty"
上記の関数は文字列のリストを取得し、それについてレポートします。
さまざまな種類の引数を受け入れるように、上記の関数シグネチャを変更します:
        def F(arg: list<any> = null_list)   # あらゆるタイプのリスト
        def F(arg: any = null)              # あらゆるタイプ

上記の例では、null リストと空リストを区別することが目的であり、null_list で
はなく null と比較することが正しい選択です。基本的な理由は、
"null_list == null" および "[] != null" であるためです。
"[] == null_list" であるため、null_list との比較は失敗します。次のセクション
で比較結果の詳細が記載されています。

                                        null-details null-anomalies
このセクションでは、null および null_<type> の使用に関する問題について説明しま
す。以下に、null 比較の列挙結果を示します。場合によっては、vim9 の null セマン
ティクスに精通している場合、プログラマは比較やその他の状況で null_<type> を使
用することを選択することがあります。

ドキュメントの他の場所には次のように書かれています:
         多くの場合、null 値は空の値と同じように処理されますが、常にそうとは限
         りません
以下に例を示します:
        vim9script
        var s1: list<string>
        var s2: list<string> = null_list
        echo s1             # 出力: "[]"
        echo s2             # 出力: "[]"

        echo s1 + ['a']     # 出力: "['a']"
        echo s2 + ['a']     # 出力: "['a']"

        echo s1->add('a')   # 出力: "['a']"
        echo s2->add('a')   # E1130: Can not add to null list

null_<type> に等しい 2 つの値は、必ずしも互いに等しいとは限りません:
        vim9script
        echo {} == null_dict      # true
        echo null_dict == null    # true
        echo {} == null           # false

他のコンテナとは異なり、初期化されていない文字列は null と等しくなります。is
演算子を使用して、null_string かどうかを判断できます:
        vim9script
        var s1: string
        var s2 = null_string
        echo s1 == null         # true - これは想定外
        echo s2 == null         # true
        echo s2 is null_string  # true

        var b1: blob
        var b2 = null_blob
        echo b1 == null         # false
        echo b2 == null         # true

null_<type> に初期化された変数はすべて、null_<type> と等しく、また null と等し
くなります。例:
        vim9script
        var x = null_blob
        echo x == null_blob     # true
        echo x == null          # true

初期化されていない変数は通常、null と等しくなります。それはそのタイプによって
異なります:
        var s: string           s == null
        var b: blob             b != null   ***
        var l: list<any>        l != null   ***
        var t: tuple<any>       t != null   ***
        var d: dict<any>        d != null   ***
        var f: func             f == null
        var j: job              j == null
        var c: channel          c == null
        var o: Class            o == null

空に初期化された変数は null_<type> と同等です。null ではありません:
        var s2: string = ""       == null_string        != null
        var b2: blob = 0z         == null_blob          != null
        var l2: list<any> = []    == null_list          != null
        var t2: tuple<any> = ()   == null_tuple         != null
        var d2: dict<any> = {}    == null_dict          != null

NOTE: ジョブなどの特殊な変数はデフォルトで null 値になり、対応する空の値はあり
ません。

==============================================================================

                                                generic-functions
5. ジェネリック関数

ジェネリック関数は、引数と戻り値の型チェックを維持しながら、同じ関数を異なる型
の引数で使用できる。これにより、型安全性とコードの再利用性が確保される。


宣言
                                                generic-function-declaration
                                                E1553 E1554
ジェネリック関数の型変数は、関数名の直後に山括弧 ("<"i と ">") で囲まれた型パ
ラメータとして宣言される。複数の型パラメータはコンマで区切られる:

        def[!] {funcname}<{type} [, {types}]>([arguments])[: {return-type}]
            {function body}
        enddef
                                                generic-function-example
これらの型パラメータは、他の型と同様に、関数シグネチャとその本体内で使用でき
る。以下の例では、2 つのリストを tuple のリストに結合している: >vim9

        vim9script
        def Zip<T, U>(first: list<T>, second: list<U>): list<tuple<T, U>>
            const LEN: number = ([first->len(), second->len()])->min()
            final result: list<tuple<T, U>> = []
            for i in range(LEN)
                result->add((first[i], second[i]))
            endfor
            return result
        enddef
        var n: list<number> = [61, 62, 63]
        var s: list<string> = ['a', 'b', 'c']
        echo $"Zip example #1: {Zip<number, string>(n, s)}"
        echo $"Zip example #2: {Zip<string, number>(s, n)}"
<
                                                type-variable-naming E1552
                                                type-parameter-naming
前の例と同様に、名前には大文字を 1 文字だけ使用するのが慣例である (例えば、T、
U、Aなど)。複数の文字で構成されている場合もあるが、名前は必ず大文字で始まる必
要がある。この例では、"Ok" は有効だが、"n" は無効である: >vim9

        vim9script
        def MyFail<Ok, n>(): void
        enddef
        # E1552: Type variable name must start with an uppercase letter: n...
<
                                                E1558 E1560
関数は、ジェネリック関数または通常の関数のいずれかとして宣言および使用する必要
がある。両方を同時に使用することはできない。以下の Vim9 script は、これらのエ
ラーを示している: >vim9

        vim9script
        My1558<number>()
        # Vim(eval):E1558: Unknown generic function: My1558
< >vim9
        vim9script
        def My1560(): void
        enddef
        My1560<string>()
        # Vim(echo):E1560: Not a generic function: My1560
<
                                                E1561
型パラメータ名は他の識別子と競合してはならない: >vim9

        vim9script
        def My1561<D, E, D>(): D
        enddef
        # E1561: Duplicate type variable name: D

        vim9script
        enum E
          Yes, No
        endenum
        def My1041<E>(): E
        enddef
        # E0141: Redefining script item "E"
<

ジェネリック関数の呼び出し
                                                generic-function-call
ジェネリック関数を呼び出すには、関数名と引数リストの間に "<" と ">" で具体的な
型を指定する:

        MyFunc<number, string, list<number>>()

        NOTE: このセクションには、generic-function-example など、ソースとし
              て参照できる実用的な例がいくつかある。

                                        E1555 E1556 E1557 E1559
関数に渡される型引数の数は、宣言された型パラメータの数と一致している必要があ
る。空の型リストは許可されない。例: >vim9

        vim9script
        def My1555<>(): void
        enddef
        # E1555: Empty type list specified for generic function ...
< >vim9
        vim9script
        def My1556<T>(): void
        enddef
        My1556<bool, bool>()
        # E1556: Too many types specified for generic function ...
< >vim9
        vim9script
        def My1557<T, U>(): void
        enddef
        My1557<bool>()
        # E1557: Not enough types specified for generic function ...
< >vim9
        vim9script
        def My1559<T>(): T
        enddef
        My1559()
        # Vim(eval):E1559: Type arguments missing for generic function ...
<
任意の Vim9 型 (vim9-types) は、ジェネリック関数内の具体的な型として使用でき
る。

スペースは使用できない:
- 関数名と "<" の間 (E1068)
- ">" と "(" の間 (E1068)、または
- "<" と ">" 内。ただし、型を区切るコンマの後に必須の箇所を除く (E1202)。

ジェネリック関数は通常の関数と同様に export および import できる。:export お
よび :import を参照。

ジェネリック関数は、別の通常関数またはジェネリック関数内で定義できる。
例: >vim9
        vim9script
        def Outer(): void
          # Returns either the first item of a list or a default value
          def FirstOrDefault<T, U>(lst: list<T>, default: U): any
            return lst->len() > 0 ? lst[0] : default
          enddef
          echo FirstOrDefault<string, bool>(['B', 'C'], false)  # echos B
          echo FirstOrDefault<number, number>([], 42)           # echos 42
        enddef
        Outer()
<

型変数を型引数として使用する

型変数は型引数として渡すこともできる。例: >vim9

        vim9script
        # T は型パラメータとして宣言されている
        # これは 'value' パラメータと戻り値の型に使用される
        def Id<T>(value: T): T
            return value
        enddef
        # U は型パラメータとして宣言されている
        # これは 'value' パラメータと戻り値の型に使用される
        def CallId<U>(value: U): U
            # U は型引数として渡される/使用される型変数である
            return Id<U>(value)
        enddef
        echo CallId<string>('I am') .. ' ' .. CallId<number>(42)

これは、リストの辞書や、以下の例のように辞書のリストのような複雑なデータ構造に
役立つ: >vim9

        vim9script
        def Flatten<T>(x: list<list<T>>): list<T>
            final result: list<T> = []
            for inner in x
                result->extend(inner)
            endfor
            return result
        enddef
        const ENGLISH: list<dict<string>> = [{1: 'one'}, {2: 'two'}]
        const MANDARIN: list<dict<string>> = [{1: '壹'}, {2: '贰'}]
        const ARABIC_N: list<dict<number>> = [{1: 1}, {2: 2}]
        echo Flatten<dict<string>>([ENGLISH, MANDARIN])
        echo Flatten<dict<any>>([ENGLISH, ARABIC_N])
<
"Flatten<T>" では、"T" は宣言された型パラメータである。関数内の他の場所では、
"T" はその型パラメータを参照する型変数である。


ジェネリッククラスメソッド

Vim9 クラスのメソッドはジェネリック関数になることができる: >vim9

        vim9script
        class Config
            var settings: dict<any>
            def Get<T>(key: string): T
                return this.settings[key]
            enddef
        endclass
        var c: Config = Config.new({timeout: 30, debug: true})
        echo c.Get<number>('timeout')
        echo c.Get<bool>('debug')
<
                                                E1432 E1433 E1434
基底クラスのジェネリッククラスメソッドは、子クラスのジェネリックメソッドでオー
バーライドできる。型変数の数は両方のメソッド間で一致している必要がある。具象ク
ラスメソッドをジェネリックメソッドでオーバーライドすることはできない。逆もまた
同様である。

ジェネリック関数参照

関数参照 (Funcref) はジェネリック関数として使用できる。これにより、特定の型
を操作する関数のファクトリーを作成できる: >vim9

        vim9script
        # 文字列内の指定された文字、またはリスト内の文字の 10 進数値と一致す
        # る。Note: '*' は 10 進数の 42 (U+002A)
        var c: string = "*"
        var char_dec: tuple<string, string> = (c, c->char2nr()->string())
        def Matcher<T>(pattern: string): func(T): bool
            return (value: T): bool => match(value, pattern) >= 0
        enddef
        var StringMatch = Matcher<string>(char_dec[0])
        echo "*+"->StringMatch()                            # true (has *)
        echo ",-"->StringMatch()                            # false
        var ListMatch = Matcher<list<number>>(char_dec[1])
        echo [42, 43]->ListMatch()                          # true (has 42)
        echo [44, 45]->ListMatch()                          # false
<

ジェネリック関数のコンパイルと逆アセンブル

:defcompile コマンドは、具体的な型の特定のリストを持つジェネリック関数をコン
パイルするために使用できる:

        defcompile MyFunc<number, list<number>, dict<string>>

:disassemble コマンドを使用すると、ジェネリック関数に対して生成された命令を
一覧表示できる:

        disassemble MyFunc<string, dict<string>>
        disassemble MyFunc<number, list<blob>>


制限事項と今後の課題

現在、Vim は以下をサポートしていない:
   - 型変数の型推論: ジェネリック関数を呼び出す際は、すべての型を明示的に指定
     する必要がある。
   - 型制約: 型変数を特定のクラスまたはインターフェイスに制限することはできな
     い (例: T extends SomeInterface)。
   - デフォルト型引数: 型パラメータが明示的に指定されていない場合に、デフォル
     トの型を提供する。

==============================================================================

6. 名前空間、Import と Export
                                        vim9script vim9-export vim9-import

Vim9 script は、import されるように書くことができます。これは、いくつかの項目
が意図的に export され、他のスクリプトで利用できるようになることを意味します。
export したスクリプトが他のスクリプトで import されると、これらの export され
た項目はそのスクリプトで使用することができます。その他の項目は、export したス
クリプトのスクリプトローカルのままであり、import スクリプトからアクセスするこ
とはできません。

この機構は、他のスクリプトがソース (import される) されうるスクリプトを書くた
めに存在し、他のスクリプトがあなたが望むものだけにアクセスできるようにします。
また、名前衝突の危険性があるグローバル名前空間を使用することも避けられます。例
えば、似たような機能を持つ2つのプラグインがある場合です。

グローバル名前空間を明示的に使用することで、ごまかすことができます。これは、本
当にグローバルなものだけに行うべきことです。


名前空間
                                                        vim9-namespace
import 可能なファイルを認識するためには、vim9script ステートメントがファイル
の最初のステートメントとして現れる必要があります (例外については vim9-mix を
参照)。これは Vim に、グローバルな名前空間ではなく、独自の名前空間でスクリプト
を解釈するように指示します。ファイルが次で始まる場合:
        vim9script
        var myvar = 'yes'
"myvar" はこのファイルの中にしか存在しないことになります。一方、vim9script
がなければ、他のスクリプトや関数から g:myvar として利用できます。
                                                        E1101
ファイルレベルの変数は、旧来のVim script のスクリプトローカル変数 "s:" と非常
によく似ていますが、"s:" は省略されています。そして、それらは削除することがで
きません。

Vim9 script では、グローバルな "g:" 名前空間は従来通り使用可能です。また、
"w:"、"b:"、"t: " 名前空間も使用できます。これらの共通点は、変数が宣言されてい
ないこと、特定の型を持っていないこと、削除できることです。 E1304

vim9script の副作用として、'cpoptions' オプションが Vim のデフォルト値に設定
されることが挙げられます:
        :set cpo&vim
効果の1つは、line-continuation が常に有効であることです。'cpoptions' の元の
値はスクリプトの最後に復元され、スクリプトで追加または削除されたフラグも元の値
に追加または削除されて同じ効果を得ることができます。フラグの順序は変更されるこ
とがあります。起動時に読み込まれる vimrc ファイルでは、このようなことは起こ
りません。

                                                        vim9-mix
1 つのスクリプトファイルで、旧来と Vim9 の両方の構文を使用する方法がある: >vim9

        " _legacy Vim script_ comments here
        if !has('vim9script')
           " _legacy Vim script_ comments/commands here
           finish
        endif
        vim9script
        # _Vim9 script_ commands/commands from here onwards
        echowindow $"has('vim9script') == {has('vim9script')}"
<
これにより、可能であれば Vim9 script 構文を活用するスクリプトを作成できるよう
になり、'vim9script' のないバージョンの Vim で vim9script コマンドを使用した場
合にエラーが発生するのを防ぐことができる。

Note Vim9 の構文は Vim 9 より前に変更されたため、現在の構文 ("import" ではなく
"import from" など) を使用するスクリプトはエラーをスローする可能性がある。
これを防ぐには、代わりに v:version >= 900 というチェックを行う方が安全である
("has('vim9script')" はパッチ 3965 を適用した Vim 8.2 では v:true を返すた
め)。
場合によっては、さらに後で切り捨てるのが賢明なこともある。Vim9 script の機能
セットは増え続けているため、例えば tuple が使用される場合 (Vim 9.1 パッチ 1232
で導入)、より適切な条件は以下のとおりである: >vim9

        if !has('patch-9.1.1232')
          echowindow $"Fail: Vim does not have patch 9.1.1232"
          finish
        endif
        vim9script
        echowindow $"Pass: version {v:versionlong}.  Continuing ..."
<
どちらの vim-mix 条件が使用されても、以下の 2 つの方法のいずれかでのみ機能す
る:
    1. "if" ステートメントが false と評価されると、endif までのコマンドはス
       キップされ、vim9script が実際に実行される最初のコマンドになる。
    2. "if" ステートメントは true と評価され、endif までのコマンドが実行され、
       finish は vim9script に到達する前に終了する。

Export
                                                        :export :exp
項目の export は次のように記述できます:
        export const EXPORTED_CONST = 1234
        export var someValue = ...
        export final someValue = ...
        export const someValue = ...
        export def MyFunc() ...
        export class MyClass ...
        export interface MyClass ...
        export enum MyEnum ...
                                                        E1043 E1044
このことからわかるように、定数、変数、:def 関数、クラス、インターフェイス、
列挙型のみが export 可能です。

                                                        E1042
:export はスクリプトレベルで、Vim9 script でのみ使用できます。


Import
                                :import :imp E1094 E1047 E1262
                                E1048 E1049 E1053 E1071 E1088 E1236
export された項目は、別のスクリプトで import することができます。import 構文に
は、2つの形式があります。単純な形式は:
        import {filename}

ここで {filename} は文字列として評価される式でなければなりません。この形式では、
ファイル名は ".vim" で終わる必要があり、".vim" より前の部分が名前空間のスクリ
プトローカル名となります。例:
        import "myscript.vim"

これにより、"myscript.vim" に export された各項目は、"myscript.item" 等として
利用可能になります。
                                                :import-as E1257 E1261
名前が長い場合や曖昧な場合は、この形式を使用して別の名前を指定することができま
す:
        import {longfilename} as {name}

この形式では、{name} が import された名前空間の特定のスクリプトローカル名とな
ります。したがって、{name} は internal-variables のように、文字、数字、'_'
で構成されなければなりません。{longfilename} 式は、任意のファイル名で評価する
必要があります。例:
        import "thatscript.vim.v2" as that
                                                E1060 E1258 E1259 E1260
"that.item" 等として使えます。"that" という名称は自由です。import したスクリプ
トを指していると認識されるようなものを使ってください。コマンド名、コマンド修飾
語、組み込み関数名などは避けてください。名前がそれらと被るからです。名前を大文
字で始めないほうがいいです。大文字で始めると、グローバルなユーザーコマンドや関
数と被る可能性があります。また、その名前を関数や変数名など、スクリプト内の他の
何かに使用することはできません。

名前のドットが望ましくない場合は、関数のローカル参照を作成できます:
        var LongFunc = that.LongFuncName

これは定数に対しても機能します:
        const MAXLEN = that.MAX_LEN_OF_NAME

これは変数には使えません。なぜなら、値は一度コピーされ、変数を変更するときは元
の変数ではなく、コピーが変更されるからです。ドットを含む完全な名前を使用する必
要があります。

関数内で :import を使用することはできません。import された項目はスクリプトレ
ベルに存在し、一度だけ import されることを意図しています。

import の後のスクリプト名は次のようになります:
- "." または ".." で始まる相対パス。スクリプトファイル自体の位置から相対的に
  ファイルを検索する。大きなプラグインを複数のファイルに分割して使用する場合に
  便利である。
- 絶対パス。Unixでは "/"、MS-Windowsでは "D:/" で始まる。これはほとんど使用さ
  れない。
- 相対パスでも絶対パスでもないパス。これは、'runtimepath' エントリの "import"
  サブディレクトリで見つかる。間違ったファイルを読み込まないようにするため、通
  常、名前は長く、一意であるべきである。
  Note "after/import" は使われない。

名前が ".vim" で終わらない場合は、"as name" を使用する必要があります。

Vim9 script ファイルを一度 import すると、その結果はキャッシュされ、次に同じス
クリプトを import するときに使用されます。再度読み込まれることはありません。

2 つの異なる "as" 名を使用する場合も同様に、同じスクリプトを 2 回 import する
ことはできません。

import 名を使用する場合、ドットと項目名は同じ行になければならず、改行すること
はできません:
        echo that.
                name   # エラー!
        echo that
                .name  # エラー!
                                                import-map
あるスクリプトから Vim9 script に関数を import した場合、import した関数の前に
<SID> を付けることで、マッピングで参照することができます:
        noremap <silent> ,a :call <SID>name.Function()<CR>

マッピングが定義されると、"<SID>name." は <SNR> とimport されたスクリプトのス
クリプトIDに置き換えられます。
さらに簡単な方法として、<ScriptCmd> を使用することができます:

        noremap ,a <ScriptCmd>name.Function()<CR>

Note これは変数には効かず、関数にしか効かないということです。

                                            import-legacy legacy-import
:import は旧来の Vim script でも使用することができます。"s:" というプリフィ
ックスがない場合でも、import された名前空間はスクリプトローカルとなります。
例:
        import "myfile.vim"
        call s:myfile.MyFunc()

そして "as name" 形式を使用する:
        import "otherfile.vim9script" as that
        call s:that.OtherFunc()

ただし、名前空間を単独で解決することはできません:
        import "that.vim"
        echo s:that
        " ERROR: E1060: Expected dot after name: s:that

これは、旧来のマッピングコンテキストにおける <SID> の使用にも影響します。
<SID> は関数に対してのみ有効なプリフィックスであり、名前空間に対しては有効で
はありません、スクリプトローカルな名前空間内の関数をスコープするために使用する
ことはできません。関数の前に <SID> を付ける代わりに、<ScriptCmd> を使用す
る必要があります。例:

        noremap ,a <ScriptCmd>:call s:that.OtherFunc()<CR>

                                                        :import-cycle
import コマンドは、遭遇したときに実行されます。スクリプト A がスクリプト B
を import し、B が (直接または間接的に) A を import した場合、これはスキップさ
れます。この時点では、"import B" 以降の A 内の項目はまだ処理されておらず、定義
されていません。したがって、循環的な import が存在しても、直接的にはエラーにな
りませんが、"import B" 以降の A 内の項目が定義されていないためにエラーになる可
能性があります。これはオートロード import には適用されません。次項参照。


オートロードスクリプトを import する
                                        vim9-autoload import-autoload
起動速度を最適化するために、スクリプトの読み込みは実際に必要になるまで延期する
必要があります。オートロード機構を使用することをお勧めします:
                                                        E1264
     1. プラグインでは、オートロードスクリプトから import された項目を参照する
        ユーザーコマンド、関数、マッピングを定義する。

        import autoload 'for/search.vim'
        command -nargs=1 SearchForStuff search.Stuff(<f-args>)

         これは、.../plugin/anyname.vim に入る。"anyname.vim" は自由に選ぶこと
        ができる。"SearchForStuff" コマンドが利用できるようになった。

        import の引数 "autoload" は、項目の1つが実際に使用されるまでスクリプ
        トがロードされないことを意味する。スクリプトは "import" ディレクトリ
        ではなく、'runtimepath' の "autoload" ディレクトリの下に見つかる。
        あるいは、相対名または絶対名のいずれかを使用することもできる。以下を参
        照。

     2. オートロードスクリプトに、コードの大部分を置きます。
        vim9script
        export def Stuff(arg: string): void
          ...

        これは、.../autoload/for/search.vim に入ります。

        "search.vim" スクリプトを "/autoload/for/" ディレクトリに置くと、
        export された項目に "for#search#" というプリフィックスが付くようになり
        ます。プリフィックスは、旧来のオートロードスクリプトで手動で追加するの
        と同じように、ファイル名から取得される。したがって、export された関数
        は"for#search#Stuff" で見つけることができますが、通常は
        import autoload を使用し、プリフィックスを使用しません (この名前に遭
        遇した関数をコンパイルするときにオートロードスクリプトをロードするとい
        う副作用が発生します)。

        機能を分割して、オートロードスクリプトから他のスクリプトを好きなように
        import することができます。こうすることで、プラグイン間でコードを共有
        することができます。

'runtimepath' のすべてのエントリからオートロードスクリプトを検索するのは、少し時
間がかかることがあります。プラグインがスクリプトの場所を知っている場合、多くの
場合、相対パスが使用されます。これにより、検索が回避され、かなり速くなるはずで
す。また、スクリプト名が一意である必要がないことも利点です。絶対パスも可能です。
例:
        import autoload '../lib/implement.vim'
        import autoload MyScriptsDir .. '/lib/implement.vim'

import したオートロードスクリプトを使用するマッピングを定義するには、特殊キー
<ScriptCmd> が便利です。これは、マッピングのコマンドで、マッピングが定義され
た場所のスクリプトコンテキストを使用することを可能にします。

:def 関数をコンパイルしているときに、オートロードスクリプトの関数に遭遇する
と、:def 関数が呼び出されるまでスクリプトはロードされません。これは、引数や
戻り値の型がまだ分かっていないため、実行時にのみエラーが発生することも意味しま
す。もし、'#' 文字を含む名前を使用するのであれば、そのオートロードスクリプトは
ロードされます。

オートロードスクリプトの中で、意図せずロードのきっかけとなる項目を参照しないよ
うに注意してください。例えば、関数名を取るオプションを設定する場合、関数参照で
はなく、必ず文字列を使用してください:
        import autoload 'qftf.vim'
        &quickfixtextfunc = 'qftf.Func'  # オートロードスクリプトはロードされ
                                         # ない
        &quickfixtextfunc = qftf.Func    # オートロードスクリプトはロードされる
一方で、スクリプトを早めに読み込むことで、エラーが発生した場合に、その旨を伝え
ることができるようになり便利です。

test_override() のテスト用関数を使用して、import autoload にスクリプトをす
ぐにロードさせることができます。これにより、項目と型が実際に使用されるのを待た
ずにチェックできるようになります:
        test_override('autoload', 1)
後でリセットします:
        test_override('autoload', 0)
または:
        test_override('ALL', 0)


==============================================================================

7. クラスとインターフェイス                             vim9-classes

旧来の Vim script では、関数であるメンバーを追加することで、辞書を一種のオブ
ジェクトとして使用することができます。しかし、これは非常に非効率的で、すべての
オブジェクトが正しいメンバーを持っていることを確認する作業を作成者が行う必要が
あります。Dictionary-function を参照。

Vim9 script では、一般的なオブジェクト指向プログラミング言語のように、クラス、
オブジェクト、インターフェイス、および列挙型を持つことができます。これは多くの
機能を含んでいるため、別のヘルプファイルに記載されています: vim9class.txt


==============================================================================

8. 理論的根拠                                           vim9-rationale

:def コマンド

プラグインの作者から、Vim script をもっと速くしたいとの要望がありました。関数
呼び出しの既存のセマンティクスを維持することは、関数の呼び出し、ローカル関数ス
コープの設定、行の実行に伴うオーバーヘッドのため、不可能に近いことが調査により
判明しました。エラーメッセージや例外など、処理しなければならない細部はたくさん
あります。a: および l: スコープ用の辞書を作成する必要性、a:000 のリスト、その
他いくつかの回避できないオーバーヘッドを追加しすぎています。

したがって、新しいスタイルの関数を定義するための :def メソッドを追加する必要
があり、これにより異なるセマンティクスを持つ関数を定義することができるようにな
りました。ほとんどのことは以前と同じように動作しますが、そうでない部分もありま
す。関数を定義する新しい方法は、旧来のスタイルのコードとVim9スタイルのコードを
分離するための最良の方法であると考えられていました。

"def" を使用して関数を定義するのは、Python に由来します。他の言語は、従来の
Vim script と競合する "function" を使用します。


型チェック

Vim コマンドの行を命令にコンパイルする場合、可能な限りコンパイル時に行うべきで
す。これを実行時に先送りすると、実行速度が遅くなり間違いに気づくのが遅くなりま
す。例えば、"+" 文字に遭遇し、これを一般的な加算命令にコンパイルする場合、実行
時に引数の型を検査し、どのような加算を行うかを決定する必要があります。そして、
その型が辞書であった場合には、エラーを投げます。もし、型が数値であることが分か
っていれば、「数値の加算」命令を使うことができ、その方が高速です。エラーはコン
パイル時に発生し、2 つの数値の加算が失敗することはほとんどないため、実行時にエ
ラー処理は必要ない。

    NOTE: 余談だが、例外として整数オーバーフローがある。これは、結果が整数の最
    大値を超える場合である。例えば、64 ビット符号付き整数に加算した結果が 2^63
    を超える場合を考える: >vim9

        vim9script
        echo 9223372036854775807 + 1     # -9223372036854775808
        echo 2->pow(63)->float2nr() + 1  # -9223372036854775808
<
複合型に <type> を使った型の構文は、Javaと似ています。理解しやすく、広く使われ
ています。型名は、以前 Vim で使われていたものに、"void" や "bool" などが追加さ
れています。


乱雑と奇妙さを取り除く

:def 関数が旧来の関数と異なる構文であることが決まれば、一般的なプログラミン
グ言語を知っているユーザーにとってより馴染みやすいコードにするための改良を加え
ることは自由です。言い換えれば、Vim でしかしないような奇妙なことは削除します。

また、Vim script に古き良きViコマンドと後方互換性を持たせるために行われたもの
を中心に、雑多なものを取り除くことができます。


例:
- 関数を呼び出すための :call と式を評価するための :eval を削除します。
- 行の継続のための先頭のバックスラッシュを使用しない。式がどこで終わるかを自動
  的に把握します。

しかし、そのためには、いくつかのことを変える必要があります:
- コメントは、文字列と混同しないように、" の代わりに # で始まるようにしました。
  これはとにかく良いことで、いくつかの人気のある言語でも使用されています。
- Ex コマンドの範囲は、式との混同を避けるため、先頭にコロンを付ける必要があり
  ます (シングルクォートは文字列またはマーク、"/" は割り算または検索コマンド、
  等)。


目標は、違いを制限することです。良い基準は、古い構文を誤って使用した場合に、
エラーメッセージが表示される可能性が非常に高いことです。


人気のある言語から構文とセマンティクスを学ぶ

スクリプトの作成者は、Vim script の構文が慣れ親しんでいるものとは予想外に異な
ると不満を漏らしています。この不満を軽減するために、人気のある言語を例として使
用します。同時に、旧来の Vim script のよく知られた部分を放棄することは避けたい
です。

いろいろなことで TypeScript が踏襲されています。これは最近人気が出てきた言語で、
Vim script に似ています。また、静的型付け (変数は常に既知の値型を持つ) と動的
型付け (変数は異なる型を持つことができ、これは実行時に変更される) が混在してい
ます。旧来の Vim script は動的型付けされており、既存の多くの機能 (特に組み込み
関数) は動的型付けに依存していますが、静的型付けはより高速に実行できるため、
Vim9 script でもこの混ぜ合わせが必要です。

TypeScript の構文やセマンティクスに完全に合わせるつもりはありません。私たちは
ただ、Vim に使える部分、Vim ユーザーが喜ぶと予想される部分を取り出したいだけで
す。TypeScript は、独自の歴史、利点と欠点を持つ複雑な言語です。デメリットを知
るには、この本を読んでください: "JavaScript: The Good Parts"。または、
"TypeScript: the good parts" という記事を見つけて、"Things to avoid" セクショ
ンを読んでください。

他の言語 (Java、Pythonなど) に慣れている人も、TypeScript の中で気に入らないこ
とや理解できないことを見つけるでしょう。そういったものを避けるようにします。

TypeScript から避ける特定の項目:
- "+" をオーバーロードして、加算と文字列連結の両方に使用します。これは旧来の
  Vim script に反しており、しばしば間違いにつながります。そのため、文字列の連
  結には ".." を使い続けることにします。Lua もこの方法で ".." を使っています。
  そして、より多くの値のために文字列に変換することができます。
- TypeScript では、"99 || 'yes'" のような式を条件として使うことができますが、
  その値を真偽値に代入することができません。これは一貫性がなく、迷惑なことで
  す。Vim は && や || を使った式を認識し、その結果を bool として使うことができ
  ます。デフォルト値を使う仕組みとして、falsy-operator が追加されました。
- TypeScript では、空の文字列を Falsy と見なしますが、空のリストや辞書を
  Truthy と見なしています。これは矛盾しています。Vimでは、空のリストや辞書も
  Falsy となります。
- TypeScript にはさまざまな "読み出し専用" 型がありますが、型キャストによって
  不変な性質を取り除くことができるため、有用性は限定的です。Vim は値をロックす
  るので、より柔軟性がありますが、実行時にしかチェックされません。
- TypeScript には複雑な "import" 文があり、Vim の import の仕組みとマッチしま
  せん。代わりに、import されたスクリプトが一度だけ読み込まれることにマッチす
  る、よりシンプルなメカニズムが使用されています。


宣言

旧来の Vim script では、すべての代入に :let を使用しますが、Vim9 では宣言が
使用されます。これは異なるので、別のコマンドを使うのがよいでしょう。var で
す。これは多くの言語で使用されています。セマンティクスは若干異なるかもしれませ
んが、宣言として簡単に認識することができます。

定数に :const を使うのは一般的ですが、そのセマンティクスは様々です。変数だけ
を不変にする言語もあれば、値も不変にする言語もあります。Java では "final" が変
数だけを不変にするためによく知られているので、これを使うことにしました。そし
て、:const を使うことで、両方を不変にすることができます。これは旧来の Vim
script でも使われていたもので、意味はほぼ同じです。

最終的に得られるものは、Dart と非常によく似ています:
        :var name       # 可変な変数と値
        :final name     # 不変な変数、可変な値
        :const name     # 不変な変数と値

旧来と Vim9 script が混在し、グローバル変数が共有されるため、オプションで型
チェックを行うことが望ましいです。また、型推論により、多くの場合、型を指定する
必要がなくなります。宣言に型を追加するには、TypeScript の構文が最も適していま
す:
        var name: string          # 文字列型が指定される
        ...
        name = 'John'
        const greeting = 'hello'  # 文字列型が推測される

これは、型を宣言に入れる方法です:
        var mylist: list<string>
        final mylist: list<string> = ['foo']
        def Func(arg1: number, arg2: string): bool

2つの選択肢を検討しました:
    1. Dart のように名前の前に型を置く:
        var list<string> mylist
        final list<string> mylist = ['foo']
        def Func(number arg1, string arg2) bool
     2. 変数名の後に型を入れるが、Go のようにコロンを使わない:
        var mylist list<string>
        final mylist list<string> = ['foo']
        def Func(arg1 number, arg2 string) bool

C や Java に慣れている人には1つ目の方が馴染みやすいと思います。2つ目は1つ目に
対しての優位性があまりないので、2つ目は捨ててしまいましょう。

型推論を使うので、値から推論できる場合は型は省くことができます。つまり、var
の後に型が続くのか名前が続くのかが分からないのです。そのため、Vim だけでなく人
間にとっても構文解析が難しくなります。また、型名になりうる変数名を使うことは許
されないでしょう。var string string を使うのはあまりにも紛らわしいです。

コロンを使用して名前と型を区切る選ばれた構文は、句読点を追加しますが、実際に
は、宣言の部分を認識しやすくします。




式の評価は、すでに他の言語が行っているものに近いものでした。いくつかの細部は予
期せぬものであり、改善することができます。例えば、真偽値条件では、文字列を受け
取り、それを数値に変換して、その数値が0でないかどうかをチェックします。これは
予想外のことで、しばしば間違いにつながります。なぜなら、数字で始まらないテキス
トはゼロに変換され、これは偽とみなされるからです。このように、条件に文字列を使
うと、エラーが出ず、偽とみなされることが多いのです。これは紛らわしいです。

Vim9 では、間違いを避けるために型チェックがより厳しくなっています。例えば
:if コマンドや || 演算子など、条件が使われる場所では、真偽値のような値しか
受け付けません:
        true:  truev:true10 < 9
        false: falsev:false00 > 9
Note 数字の 0 は偽で、数字の 1 は真です。これは、他の多くの言語よりも寛容です。
これは、多くの組み込み関数がこれらの値を返すため、それを変更すると、解決するよ
りも多くの問題を引き起こすからです。これをしばらく使ってみると、うまくいくこと
が分かりました。

任意の型を持つ値について、それを真偽値として使いたい場合は、!! 演算子を使い
ます (expr-! を参照): >vim9

        vim9script
        # 以下はすべて true:
        echo [!!'text', !![1], !!{'x': 1}, !!1, !!1.1]
        # そしてこれらはすべて false:
        echo [!!'', !![], !!{}, !!0, !!0.0]
<
        true: !!'text'   !![99]   !!{'x': 1}   !!99
        false: !!''   !![]   !!{}

JavaScript のような言語には、このような便利な構文があります:
        GetName() || 'unknown'
ただし、これは、条件に真偽値のみを許可することと競合します。したがって、"??"
演算子が追加されました:
        GetName() ?? 'unknown'
ここでは、値をそのまま使用し、真偽値にしないという意図を明示的に表すことがで
きます。これは falsy-operator と呼ばれます。


Import と Export

旧来の Vim script の問題点は、デフォルトですべての関数と変数がグローバルである
ことです。スクリプトローカルにすることは可能ですが、そうすると他のスクリプトで
利用できなくなります。これは、選択された項目のみをエクスポートし、残りをローカ
ルに保つパッケージの概念に反しています。

Vim9 script では、JavaScript の import および export 機構に非常によく似た機構
がサポートされています。これは既存の :source コマンドの亜種で、期待通りに動
作します:
- デフォルトですべてをグローバルにするのではなく、すべてをスクリプトローカルに
  することで、その一部をエクスポートすることができる。
- スクリプトの import 時には、import されるシンボルが明示的にリストされるため、
  名前の衝突や、後から機能を追加した場合の失敗を回避することができる。
- この機構により、export された関数、変数、クラスという非常に明確なAPIを持つ巨
  大で長いスクリプトを書くことができる。
- 相対パスを使用することで、パッケージ内のインポートのロードが非常に速くなり、
  多くのディレクトリを検索する必要がない。
- 一度インポートを使用すると、その項目はキャッシュされ、再ロードする必要はな
  い。
- スクリプトローカルにするための Vim 特有の "s:" の使用をやめることができる。

(Vim9 script または旧来の Vim script から) Vim9 script を読み込む場合、グロー
バルに定義された項目のみが使用可能で、export された項目は使用できません。代替
案を検討しました:
- export された項目は、すべてスクリプトローカル項目として利用できるようにする。
  これにより、どの項目が定義されるかは制御できず、すぐにトラブルに発展する可能
  性がある。
- export された項目を使用し、それらをグローバルにする。デメリットは、グローバ
  ルな名前空間での名前の衝突を避けることができなくなることである。
- Vim9 script を読み込むことを完全に禁止し、:import を使うことを要求する。こ
  れにより、スクリプトをテストに使用したり、コマンドラインからスクリプトを取得
  して試したりすることが難しくなる。
Note 上記の通り、旧来の Vim script でも :import を使用することができます。


関数を早期にコンパイルする

関数は、呼び出されたとき、または :defcompile が使用されたときにコンパイルさ
れます。なぜ、早期にコンパイルして、構文エラーや型エラーが早期に報告されるよう
にしないのでしょうか?

なぜなら、後に定義される関数への前方参照が存在する可能性があるからです。例え
ば、A、B、C という関数を定義し、A が B を呼び出し、B が C を呼び出し、C がまた
A を呼び出すとします。前方参照を避けるために関数を並べ替えることは不可能です。

別の方法としては、まずファイルをスキャンして項目を見つけ、そのタイプを把握し、
前方参照が見つかるようにしてから、スクリプトを実行し、関数をコンパイルする方法
があります。この場合、スクリプトを2回解析する必要があり、遅いです。また、機能
がサポートされているかどうかのチェックなど、スクリプトレベルでの条件もあるた
め、使いにくいです。うまくいくかどうか試行錯誤しましたが、うまく動作させること
は不可能であることが判明しました。

スクリプトの最後にすべての関数をコンパイルすることは可能でしょう。しかし、ある
関数が一度も呼び出されない場合、その関数をコンパイルするためのオーバーヘッド
が、いずれにせよカウントされてしまうという欠点があります。起動速度は非常に重要
なので、ほとんどの場合、コンパイルは後回しにした方がよく、構文や型のエラーはそ
の時にしか報告されないことを受け入れることができます。例えばテスト時など、これ
らのエラーを早期に発見する必要がある場合は、スクリプトの最後の :defcompile
コマンドが効果的です。


なぜ既存の組み込み言語を使わないのか?

Vim は Perl、Python、Lua、Tcl、その他いくつかへのインターフェイスをサポートし
ています。しかし、これらのインターフェイスは様々な理由で広く使われるようになる
ことはありませんでした。Vim9 が設計されたとき、これらのインターフェイスの優先
順位を下げ、Vim script に集中させることが決定されました。

それでも、プラグイン作成者は他の馴染みのある言語を探したり、既存のライブラリを
使用したり、性能の恩恵を確認したりするかもしれません。我々は、プラグインの作者
が任意の言語でコードを書き、ジョブやチャネルを使って外部プロセスとして実行する
ことを推奨します。我々は、これをどうにかして簡単にできるように努力することがで
きます。

外部ツールの使用にもデメリットがある。代替案として、ツールを Vim Script に変換
する方法がある。これを実現するには、余分な翻訳作業を避けつつコードの高速性を維
持する必要があり、ツールの構造をサポートする必要がある。Vim9 Script ではクラ
ス、オブジェクト、インターフェイス、列挙型がサポートされているため、これはます
ます実現可能になっている。


 vim:tw=78:ts=8:noet:ft=help:norl: