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 (および vim9cmd で始
        まる個々の行) で始まる 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
                Vim9 script の構文、セマンティクス、および動作を使用して {cmd}
                を評価し、実行する。:function や旧来の Vim script 内でコマン
                ドを入力するときに便利である。

                次の短い例は、旧来の Vim script コマンドと :vim9cmd (つまり
                Vim9 script コンテキスト) が似ているように見えるものの、構文だ
                けでなく意味や動作も異なる可能性があることを示している。 >vim

                  call popup_notification('entrée'[5:]
                    \ ->str2list()->string(), #{time: 7000})
                  vim9cmd popup_notification('entrée'[5 :]
                      ->str2list()->string(), {time: 7000})
<
                 Notes: 1) 出力が異なる理由は、Vim9 script は文字インデックス
                           を使用するのに対し、旧来の Vim script はバイトイン
                           デックスを使用するためである。vim9-string-index
                           を参照。
                        2) 構文も異なる。Vim9 script では:
                          - "[5 :]" 内のスペースは必須である
                            (vim9-white-space を参照)。
                          - "\" による行継続は必須ではない。
                          - "#" (辞書キーを引用符で囲むのを避けるため) は必須
                            ではなく、許可もされない - #{} を参照。

                                                E1164
                :vim9cmd は単独では実行できない。コマンドが後に続く必要があ
                る。

:leg[acy] {cmd}                                 :leg :legacy
                {cmd} を旧来の Vim script の構文、意味、および動作を使用して
                評価および実行する。これは、Vim9 script または :def 関数での
                み適用できる。上記のスクリプトと同等のスクリプトを使用する場合
                (出力が異なる理由については、スクリプトの注記を参照): >vim9

                  vim9script
                  # レガシーコンテキスト - つまり、これは [769, 101] のポップ
                  # アップを作成する
                  legacy call popup_notification('entrée'[5:]
                    \ ->str2list()->string(), #{time: 7000})
                  # Vim9 script コンテキスト - これは [101] のポップアップを作
                  # 成する
                  popup_notification('entrée'[5 :]
                      ->str2list()->string(), {time: 7000})
<
                Vim9 script のスクリプトローカル変数は、旧来の Vim script と同
                様に、プリフィックスに "s:" を付けることで使用できる。この例
                は、構文の違いを示している。Vim9 script ではスクリプトローカル
                変数は "k" だが、旧来の Vim script では "s:k" である。 >vim9

                  vim9script
                  var k: string = "Okay"
                  echo k
                  legacy echo s:k
<                                               E1189
                :legacy はコンパイルされた Vim9 script の制御フローコンテキ
                ストでは使用できない。例: >vim9

                  vim9script
                  def F_1189()
                    if v:version == 900
                    # E1189: Cannot use :legacy with this command: endif
                    legacy endif
                  enddef
                  F_1189()
<                                               E1234
                :legacy は単独では実行できない。コマンドが後に続く必要があ
                る。


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

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

概要
                                                        E1146
Vim9 script と :def 関数を使用するときに最も頻繁に遭遇する違いの簡単な概要。
詳細は以下の通り:
- コメントは " ではなく、# で始める:
        echo "hello"   # コメント
- 行継続にバックスラッシュを使用する必要はほとんどない:
        echo "hello "
             .. yourName
             .. ", how are you?"
- 読みやすさを向上させるために、多くの場所で空白が必要である。
  vim9-white-space を参照。
: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

引数の型と戻り値の型を指定する必要がある。"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 用
                                                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: Cannot add to null list

                                                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 のようにコマン
  ドが別のコマンドの引数として使用されている場合には、その傾向が顕著である。そ
  のような場合は、バックスラッシュによる行継続を使用する必要がある。


空白
        vim9-white-space 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
:def[!] {name}([arguments])[: {return-type}]
                        {name} という名前で新しい関数を定義する。関数本体は、
                        対応する :enddef まで、次の行に記述する。
                                                        E1073
                        {name} はスクリプトローカルレベルでは再利用できない: >vim9

                          vim9script
                          def F_1073()
                          enddef
                          def F_1073() # E1073: Name already defined: <SNR>...
                          enddef
<                                                       E1011
                        {name} の長さは 100 バイト未満である必要がある。

                                                        E1077
                        {arguments} は、0 個以上の引数宣言の並びである。以下の
                        3 つの形式がある:
                                {name}{type}
                                {name} = {value}
                                {name}{type} = {value}
                        最初の形式は必須の引数である。そのため、宣言では型を指
                        定する必要がある。例: >vim9

                          vim9script
                          def F_1077(x): void
                              # E1077: Missing argument type for x
                          enddef
<
                        2 番目の形式の場合、宣言で型が指定されていないため、
                        Vim は型を推測する。2 番目と 3 番目の形式の両方におい
                        て、呼び出し側で {value} が省略された場合はデフォルト
                        の {value} が適用される。例: >vim9

                          vim9script
                          def SecondForm(arg = "Hi"): void
                              echo $'2. arg is a "{arg->typename()}" type ' ..
                                   $'and the default value of arg is "{arg}"'
                          enddef
                          SecondForm()
                          def ThirdForm(arg2: number = 9): void
                              echo $'3. default value of arg2 is {arg2}'
                          enddef
                          ThirdForm()
<                                                       E1123
                        :def 関数で呼び出される組み込み関数の引数は、引数間
                        にコンマが必要である: >vim9

                          vim9script
                          def F_1123(a: number, b: number): void
                              echo max(a b)
                              # E1123: Missing comma before argument: b)
                          enddef
                          F_1123(1, 2)
<                                                       E1003 E1027 E1096
                        :return で使用される値の型は {return-type} と一致す
                        る必要がある。{return-type} が省略されているか "void"
                        の場合、関数は何も返せない。例: >vim9

                          vim9script
                          def F_1003(): bool
                              return  # E1003: Missing return value
                          enddef
                          F_1003()
< >vim9
                          vim9script
                          def F_1027(): bool
                              echo false  # E1027: Missing return statement
                          enddef
                          F_1027()
< >vim9
                          vim9script
                          def F_1096(): void
                              return false  # E1096: Returning a value ...
                          enddef
                          F_1096()
<                                                       E1056 E1059
                        ": {return-type}" が指定されている場合、{return-type}
                        は省略できない (ぶら下がりコロンが残る)。また、": " の
                        前に空白文字を置くこともできない。例: >vim

                          def F_1056():
                              # E1056: Expected a type:
                          enddef
                          def F_1059() : bool
                              # E1059: No white space allowed before colon:...
                          enddef
<
                        関数は、呼び出されたとき、または :defcompile または
                        :disassemble が使用されたときに命令にコンパイルされ
                        る。(例については、:disassemble を参照。) その際に構
                        文エラーと型エラーが生成される。

                                                        E1058
                        :def を別の :def または :function 内にネストでき
                        るのは最大 49 レベルまでである。50 レベルを超えると
                        E1058 エラーが発生する。

                                                        E1117
                        [!] は、関数の再定義 (:function! と同様) を許可する
                        ため、旧来の Vim script でのみ使用できる。Vim9 script
                        では、スクリプトローカルな関数は削除または再定義できな
                        いため、! は使用できない。ただし、スクリプトを再読み込
                        みすることで削除できる。また、ネストされた関数では、再
                        定義に ! は使用できない。例: >vim

                          " 旧来の Vim script の :def! の例
                          def! LegacyFunc()
                              echo "def! is allowed in a legacy Vim script"
                          enddef
                          call LegacyFunc()
< >vim9
                          vim9script
                          def Func()
                              def! InnerFunc()
                                  # E1117: Cannot use ! with nested :def
                              enddef
                          enddef
                          Func()
< >vim9
                          vim9script
                          def! F_477(): void  # E477: No ! allowed
                          enddef
< >vim9
                          vim9script
                          def F_1084(): void
                          enddef
                          delfunction! F_1084
                          # E1084: Cannot delete Vim9 script function F_1084
<
                        Note: 一般的なエラー E1028 ("Compiling :def function
                        failed") は、コンパイル中に発生した原因不明のエラーを
                        示す。再現可能な場合は、Vim のエラー報告に欠陥がある可
                        能性があるため、https://github.com/vim/vim/issues に報
                        告してほしい。

                                        :enddef E1057 E1152 E1173
:enddef                 :def で定義された関数の終了。単独の行に記述する必要
                        がある。例: >vim9

                          vim9script
                          def MyFunc()
                          echo 'Do Something' | enddef
                          # E1057: Missing :enddef
< >vim9
                          vim9script
                          def F_1173()
                          enddef echo 'X'
                          # E1173: Text found after enddef: echo 'X'
< >vim9
                          vim9script
                          def F_1152()
                              function X()
                              enddef  # E1152: Mismatched enddef
                          enddef
<
こちらの Wiki も役に立つかもしれない。これは Vim9 sript のアーリーアダプターに
よって書かれたものである:
https://github.com/lacygoill/wiki/blob/master/vim/vim9.md

:def 関数が定義されているスクリプトが Vim9 script の場合、スクリプトローカル
変数は "s:" プリフックスを使用せずにアクセスする必要がある。これらの変数は関数
がコンパイルされる前に定義する必要があり、エラーを回避する方法はない (例えば、
exists() を使用して未宣言の変数を条件付きでスキップするなど)。例: >vim9

        vim9script
        def MyVim9def()
            echo unus       # 1 を表示する
            # echo s:unus   # これは E1268 (Cannot use s: in Vim9) になる
            if exists('duo')
                # echo duo  # これは E1001 (Variable not found: duo) になる
            endif
        enddef
        var unus: number = 1
        MyVim9def()         # MyVim9def がコンパイルされる ("duo" はまだ存在し
                            # ていない)
        var duo: number = 2
<
:def 関数が定義されているスクリプトが旧来の Vim script である場合、スクリプ
トローカル変数は "s:" プリフィックスの有無にかかわらずアクセスできる。ただし、
"s:" を使用すると変数の解決が実行時に遅延されるため、まだ存在しない可能性のあ
る変数によるコンパイルエラーを回避できる。次の例で説明する: >vim

        " 旧来の Vim script
        def! MyLegacyDef(): void
            echo [unus, s:unus]   # [1, 1] を表示する
            # (コメントアウトされていない場合) 'echo s:duo' の最初のソースは
            # E121 となり、コンパイルエラーが発生する。後続のソースは 2 を表示
            # する
            # echo s:duo
            if exists("s:duo")
                # 最初のソース: 表示をスキップ; 後続のソース: 2 を表示
                echo s:duo
            endif
            if exists("duo")
                # (コメントアウトされていない場合) 'echo duo' の最初のソースは
                # E1001 で、コンパイルエラーが発生する。後続のソースは 2 を表
                # 示する
                # echo duo
            endif
        enddef
        let s:unus = 1
        call MyLegacyDef()  " MyLegacyDef() を呼び出し、まだコンパイルされてい
                            " ない場合はコンパイルする
        let s:duo = 2
<                                                       E1269
Vim9 script 内のスクリプトローカル変数は、スクリプトレベルで宣言する必要があ
る。:def 関数内では作成できず、また "s:" プリフィックスを持つ旧来の関数内で
も宣言できない。例: >vim9

        vim9script
        function F_1269()
            let s:i_wish = v:true
        endfunction
        F_1269()
        # E1269: Cannot create a Vim9 script variable in a function: s:i_wish
<
                                                :defc :defcompile
:defc[ompile]           現在のスクリプトで定義されていてまだコンパイルされてい
                        ない関数とクラス (class-compile) をコンパイルする。
                        これにより、コンパイル中に見つかったエラーが報告され
                        る。

                        例: (enddef までの) 3 行がソースコードに読み込まれる
                        と、Vim9 の :def 関数がコンパイルされないためエラー
                        は発生しない。しかし、4 行すべてがソースコードに読み込
                        まれると、コンパイルは失敗する: >vim9

                          vim9script
                          def F_1027(): string
                          enddef
                          defcompile F_1027  # E1027: Missing return statement

:defc[ompile] MyClass   クラス内のすべてのメソッドをコンパイルする。(例につい
                        ては :disassemble を参照。)

: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} に対して生成された命令を表示する。
                        これはデバッグとテスト用である。
                        {func} が見つからない場合、エラー E1061 が発生する。
                        {func} を "ClassName.functionName" にすることで、クラ
                        ス内の関数を逆アセンブルすることもできる。
                        次の例は、class で :defcompile を使用し、
                        "ClassName.functionName" で :disassemble を使用する
                        方法を示している (カーソルはビジュアルソースのスクリプ
                        トの最終行にある): >vim9

                          vim9script
                          class Line
                              var lnum: number
                              def new(this.lnum)
                              enddef
                              def SetLnum()
                                  cursor(this.lnum, 52)
                              enddef
                          endclass
                          defcompile Line
                          disassemble Line.SetLnum
                          var vlast: Line = Line.new(line("'>"))
                          vlast.SetLnum()  # カーソル位置はココにある-->_

:disa[ssemble] profile {func}
                        :disassemble と似ているが、プロファイリングに使用さ
                        れる命令が含まれている。

:disa[ssemble] debug {func}
                        :disassemble と似ているが、デバッグに使用される命令
                        が含まれている。

  Note{func} のコマンドライン補完では、スクリプトローカル関数が <SNR> ととも
        に表示される。wildmenumode() などのオプションによっては、"s:"、"<S"、
        または関数名を直接入力することで補完が機能する場合がある。(例えば
        -u NONE で起動した Vim では、":disa s:" と c_CTRL-E はスクリプト
        ローカル関数名を一覧表示する。)


制限事項

:def 関数のローカル変数は文字列評価では参照できない。
以下の例は、スクリプトローカル定数 "SCRIPT_LOCAL" は参照できるが、関数ローカル
定数 "DEF_LOCAL" は参照できないことを示している: >vim9

        vim9script
        const SCRIPT_LOCAL = ['A', 'script-local', 'list']
        def MapList(scope: string): list<string>
            const DEF_LOCAL: list<string> = ['A', 'def-local', 'list']
            if scope == 'script local'
                return [1]->map('SCRIPT_LOCAL[v:val]')
            else
                return [1]->map('DEF_LOCAL[v:val]')
            endif
        enddef
        echo 'script local'->MapList()  # ['script-local'] を表示する
        echo 'def local'->MapList()     # E121: Undefined variable: DEF_LOCAL
<
map 引数は文字列式であり、関数スコープ外で評価される。Vim9 script では、代わり
にラムダ式を使用する: >vim9

        vim9script
        def MapList(): list<string>
            const DEF_LOCAL: list<string> = ['A', 'def-local', 'list']
            return [1]->map((_, v) => DEF_LOCAL[v])
        enddef
        echo MapList()                  # ['def-local'] を表示する
<
:edit などのコンパイルされないコマンドの場合、backtick-expansion を使用で
き、ローカルスコープを使用できる。例: >vim9

        vim9script
        def EditNewBlah()
            var fname: string = 'blah.txt'
            split
            edit =fname
        enddef
        EditNewBlah()  # 新しい分割がバッファ 'blah.txt' として作成される
<
ループ内で定義されたクロージャは、変数の宣言場所に応じて、変数を共有するか、そ
れぞれ独自のコピーを持つかを選択できる。ループ外で変数を宣言した場合、すべての
クロージャは同じ共有変数を参照する。
以下の例は、"outloop" 変数が 1 つだけ存在する場合の結果を示している: >vim9

        vim9script
        var flist: list<func>
        def ClosureEg(n: number): void
            var outloop: number = 0  # outloop がループの外で宣言されている!
            for i in range(n)
                outloop = i
                flist[i] = (): number => outloop  # クロージャは同じ変数を参照
                                                  # する
            endfor
            echo range(n)->map((i, _) => flist[i]())
        enddef
        ClosureEg(4)  # [3, 3, 3, 3] を表示する
<
リストに置かれたすべてのクロージャは同じインスタンスを参照し、最終的には 3 に
なる。

ただし、変数がループ内で宣言されると、以下の例に示すように各クロージャは独自の
コピーを取得する: >vim9

        vim9script
        var flist: list<func>
        def ClosureEg(n: number): void
            for i in range(n)
                var inloop: number = i  # inloopはループ内で宣言されている
                flist[i] = (): number => inloop  # クロージャは各インループを
                                                 # 参照する
            endfor
            echo range(n)->map((i, _) => flist[i]())
        enddef
        ClosureEg(4)  # [0, 1, 2, 3] を表示する

各クロージャに個別のコンテキストを設定する別の方法は、関数を呼び出してそれを定
義することである: >vim9

        vim9script
        def GetClosure(i: number): func
            var infunc: number = i
            return (): number => infunc
        enddef
        var flist: list<func>
        def ClosureEg(n: number): void
            for i in range(n)
                flist[i] = GetClosure(i)
            endfor
            echo range(n)->map((i, _) => flist[i]())
        enddef
        ClosureEg(4)  # [0, 1, 2, 3] を表示する
<                                                       E1271
クロージャは、それが定義されているコンテキストでコンパイルされなければならな
い。そうすることで、そのコンテキスト内の変数を参照できるようになる。これはほと
んどの場合正しく実行されるが、関数がコンパイル後に :breakadd でデバッグ用に
マークされている場合は例外である。外側の関数をコンパイルする前に、必ずブレーク
ポイントを定義すること。
                                                        E1248
ローカル変数をキャプチャする Vim9 のクロージャを文字列に変換して実行した場合な
ど、状況によってはエラーが発生する。これは、文字列実行コンテキストが、クロー
ジャが定義された元のコンテキストからローカル変数にアクセスできないために発生す
る。例: >vim9

        vim9script
        def F_1248(): void
            var n: number
            var F: func = () => {
                n += 1
            }
            try
                execute printf("call %s()", F)
            catch
                echo v:exception
            endtry
        enddef
        F_1248()  # Vim(call):E1248: Closure called from invalid context

Vim9 script では、ループ変数はループが終了した後は無効になる。
例えば、このタイマーは 0 から 2 までを別々の行にエコーする。しかし、:endfor
の後に変数 "n" を使用すると、E121 エラーが発生する: >vim9

        vim9script
        for n in range(3)
            var nr: number = n
            timer_start(1000 * n, (_) => {
                echowindow nr
            })
        endfor
        try
            echowindow n
        catch
            echo v:exception
        endtry
<
          Note: タイマーでは :echowindow を使用すると便利である。メッセージ
                がポップアップに表示されるため、トリガー時にユーザーの操作を妨
                げない。


:function を :def に変換する
                                        convert_legacy_function_to_vim9
                                        convert_:function_to_:def
:function を :def 関数に変換するには、多くの変更が必要である。以下にいくつ
か例を挙げる。

- 変数宣言に使用されている let を varconst、または final のいずれか
  に変更し、各 script-variable から "s:" を削除する。
func または function を def に変更する。
endfunc または endfunction を enddef に変更する。
- 各関数の引数に適切な型(または "any")を追加する。
- 各 function-argument から "a:" を削除する。
:func-range:func-abort:func-dict:func-closure などの不適切なオ
  プションを削除する。
- 関数が何かを返す場合は、戻り値の型を追加する。(理想的には、何も返さない場合
  は "void" を追加する。)
- 不要な場所から行継続のバックスラッシュを削除する。
g:b:w:t:、または l: 変数に値を代入するための let を削除す
  る。
- Vim9 script 構文の lambda 式を書き直す (vim9-lambda を参照)。
- コメントを " ではなく # (先頭に空白文字) で始めるように変更する。
- 必要に応じて式に空白文字を挿入する (vim9-white-space を参照)。
- 文字列連結に使用される "." を " .. " に変更する。(または、
  interpolated-string を使用する。)

以下の旧来の Vim script と Vim9 script の例は、これらの違いをすべて示している。
まず、旧来の Vim script: >vim

        let s:lnum=0
        function Leg8(arg) abort
            let l:pre=['Result',
              \': ']
            let b:arg=a:arg
            let s:lnum+=2
            let b:arg*=4
            let l:result={pre->join(pre,'')}(l:pre)
            return l:result.(b:arg+s:lnum)"コメントの前にスペースがない
        endfunction
        call Leg8(10)->popup_notification(#{time: 3000})" 'Result: 42' をポッ
                                                        " プアップする

Vim9 scripti で同等のもの: >vim9

        vim9script
        var lnum: number
        def Vim9(arg: number): string
            final pre = ['Result',
              ': ']
            b:arg = arg
            lnum += 2
            b:arg *= 4
            const RESULT: string = ((lpre) => join(lpre, ''))(pre)
            return RESULT .. (b:arg + lnum) # #コメントの前にスペースが必要
        enddef
        Vim9(10)->popup_notification({time: 3000}) # 'Result: 42' をポップアッ
                                                   # プする

<       Note: この例では、次のことも示しています (:def 関数の外側で):
                - 旧来の #{} から "#" を削除する - vim9-literal-dict を参照
                - :call の省略 (Vim9 script では不要だが許可されている)


expr オプションで :def 関数を呼び出す
                                                        expr-option-function
'foldexpr' などのいくつかのオプションの値は、値を得るために評価される式である。
この評価にはかなりのオーバーヘッドがかかることがある。オーバーヘッドを最小限に
抑え、オプションの値をシンプルに保つ 1 つの方法は、コンパイル済みの関数を定義
し、それを引数なしで呼び出すオプションを設定することである。例: >vim9

        vim9script
        def MyFoldFunc(): string
            # これは行頭 (^) に続いて数字、ピリオド、スペースまたはタブ、大文
            # 字が続き、次の行が空であるものにマッチする
            return getline(v:lnum) =~ '^[[:digit:]]\.[[:blank:]][[:upper:]]'
                && getline(v:lnum + 1)->empty() ? '>1' : '1'
        enddef
        set foldexpr=MyFoldFunc()
        set foldmethod=expr
        norm! zM
<
  Warning: このスクリプトは、この vim9.txt ヘルプバッファの "Heading 1" レベル
           に折り畳みを作成して適用する。(スクリプトを読み込んだ後、通常モード
           で zRを使用すると、すべての折り畳むを開くことができる。)


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

4. 型                                   vim9-types

対応する内部 v:t_TYPE 変数とともに表示される以下の型がサポートされている。

        number                                  v:t_number
        string                                  v:t_string
        func                                    v:t_func
        func: {type}                            v:t_func
        func({type}, ...)                       v:t_func
        func({type}, ...): {type}               v:t_func
        list<{type}>                            v:t_list
        dict<{type}>                            v:t_dict
        float                                   v:t_float
        bool                                    v:t_bool
        none                                    v:t_none
        job                                     v:t_job
        channel                                 v:t_channel
        blob                                    v:t_blob
        class                                   v:t_class
        object                                  v:t_object
        typealias                               v:t_typealias
        enum                                    v:t_enum
        enumvalue                               v:t_enumvalue
        tuple<{type}>                           v:t_tuple
        tuple<{type}{type}, ...>              v:t_tuple
        tuple<...list<{type}>>                  v:t_tuple
        tuple<{type}, ...list<{type}>>          v:t_tuple
        void

                                                 E1031 E1186
これらの型は宣言で使用できるが、単純な値は "void" 型を持つことはできない。void
を値として使用しようとするとエラーが発生する。例: >vim9

        vim9script
        def NoReturnValue(): void
        enddef
        try
            const X: any = NoReturnValue()
        catch
            echo v:exception      # E1031: Cannot use void value
            try
                echo NoReturnValue()
            catch
                echo v:exception  # E1186: Expression does not result in a ...
            endtry
        endtry
<                                               E1008 E1009 E1010 E1012
不正な宣言や型の不一致はエラーを引き起こす。以下は、E1008、E1009、E1010、E1012
のエラーの例である: >vim9

        vim9cmd var l: list
        vim9cmd var l: list<number
        vim9cmd var l: list<invalidtype>
        vim9cmd var l: list<number> = ['42']
<
配列型はない。代わりに、リストまたはタプルを使用する。これらの型はリテラル (定
数) にもなり得る。次の例では、[5, 6] はリストのリテラル、(7, ) はタプルのリテ
ラルである。エコーされたリストもリストのリテラルである: >vim9

        vim9script
        var l: list<number> = [1, 2]
        var t: tuple<...list<number>> = (3, 4)
        echo [l, t, [5, 6], (7, )]
<
                                                        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
                                    vim9-partial-declaration
                                    vim9-func-type
関数 (または部分関数) は次の方法で宣言できる:
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 にすることもできる。この場合、追加の引数や辞書が格納されるが、
これらは呼び出し元からは参照できない。これらは同じ方法で呼び出されるため、宣言
も同じである。この対話型の例では、円の半径を入力すると、partial を使用して面積
を小数点以下 2 桁で返す: >vim9

        vim9script
        def CircleArea(pi: float, radius: float): float
            return pi * radius->pow(2)
        enddef
        const AREA: func(float): float = CircleArea->function([3.14])
        const RADIUS: float = "Enter a radius value: "->input()->str2float()
        echo $"\nThe area of a circle with a radius of {RADIUS} is " ..
             $"{AREA(RADIUS)} (π to two d.p.)"
<
                                                vim9-typealias-type
カスタム型 (typealias) は :type で定義できる。ユーザー関数と同様に、大文字
で始める必要がある (これにより、現在または将来の組み込み型との名前の衝突を回避
できる)。以下の例は、完全平方のリストを作成し、type() (14、typealias)
typename() を報告する: >vim9

        vim9script
        type Ln = list<number>
        final perfect_squares: Ln = [1, 4, 9, 16, 25]
        echo "Typename (Ln): " ..
             $"type() is {Ln->type()} and typename() is {Ln->typename()}"
<
                                                E1105
typealias 自体は文字列に変換できない: >vim9

        vim9script
        type Ln = list<number>
        const FAILS: func = (): string => {
            echo $"{Ln}"  # E1105: Cannot convert typealias to string
            }
<
                                    vim9-class-type  vim9-interface-type
                                    vim9-object-type
classobjectinterface はすべて型として使用できる。以下の対話型の例は、
float 値の入力を促し、2 つの異なる図形の面積を返す。また、クラス、オブジェク
ト、インターフェイスの type() と typename() も報告する: >vim9

        vim9script
        interface Shape
            def InfoArea(): tuple<string, float>
        endinterface
        class Circle implements Shape
            var radius: float
            def InfoArea(): tuple<string, float>
                return ('Circle (π × r²)', 3.141593 * this.radius->pow(2))
            enddef
        endclass
        class Square implements Shape
            var side: float
            def InfoArea(): tuple<string, float>
                return ('Square (s²)', this.side->pow(2))
            enddef
        endclass
        const INPUT: float = "Enter a float value: "->input()->str2float()
        echo "\nAreas of shapes:"
        var myCircle: object<Circle> = Circle.new(INPUT)
        var mySquare: object<Square> = Square.new(INPUT)
        final shapes: list<Shape> = [myCircle, mySquare]
        for shape in shapes
            const [N: string, A: float] = shape.InfoArea()
            echo $"\t- {N} has area of {A}"
        endfor
        echo "\n\t\ttype()\ttypename()\n\t\t------\t----------"
        echo $"Circle\t\t{Circle->type()}\t{Circle->typename()}"
        echo $"Square\t\t{Square->type()}\t{Square->typename()}"
        echo $"Shape\t\t{Shape->type()}\t{Shape->typename()}"
        echo $"MyCircle\t{myCircle->type()}\t{myCircle->typename()}"
        echo $"MySquare\t{mySquare->type()}\t{mySquare->typename()}"
        echo $"shapes\t\t{shapes->type()}\t{shapes->typename()}"
<
                                        vim9-enum-type  vim9-enumvalue-type
enum は型 (v:t_enum) として使用できる。enum 値を保持する変数は、実行時に
enumvalue 型 (v:t_enumvalue) を持つ。以下の対話型の例は、文字の入力を促し、
正方形または菱形に関する情報を返す。また、enum と enumvalue の type() と
typename() も報告する: >vim9

        vim9script
        enum Quad
            Square('four''only'),
            Rhombus('opposite''no')
            var eq: string
            var ra: string
            def string(): string
                return $"\nA {this.name} has " ..
                       $"{this.eq} equal sides and {this.ra} right angles\n\n"
            enddef
        endenum
        echo "Rhombus (r) or Square (s)?"
        var myQuad: Quad = getcharstr() =~ '\c^R' ? Quad.Rhombus : Quad.Square
        echo myQuad.string() .. "\ttype()\ttypename()"
        echo $"Quad  \t{Quad->type()}  \t{Quad->typename()}"
        echo $"myQuad\t{myQuad->type()}\t{myQuad->typename()}"
<
         Notes: このスクリプトは組み込みメソッド "string()" (object-string())
                を使用している。
                Quad と myQuad の typename() は同じ ("enum<Quad>") ですが、
                type() は区別される (myQuad は 16、enumvalue を返すが、Quad は
                15、enum を返す)。

変数の型と型キャスト
                                                        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
- 文字列が期待されるところで数値を用いる。

一つの影響として、型が宣言された場合は 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: