diff --git a/doc/vim9.jax b/doc/vim9.jax index c6acbac9c..6183e2640 100644 --- a/doc/vim9.jax +++ b/doc/vim9.jax @@ -1,4 +1,4 @@ -*vim9.txt* For Vim バージョン 9.1. Last change: 2025 Aug 27 +*vim9.txt* For Vim バージョン 9.1. Last change: 2025 Oct 06 VIMリファレンスマニュアル by Bram Moolenaar @@ -17,9 +17,31 @@ script の新しい文法と機能について書かれています。 5. ジェネリック関数 |generic-functions| 6. 名前空間、Import と Export |vim9script| 7. クラスとインターフェイス |vim9-classes| - 8. 理論的根拠 |vim9-rationale| + +------------------------------------------------------------------------------ + + NOTE: この vim9.txt ヘルプファイルでは、`vim9script` で始まる Vim9 script の + コードブロックは、Vim9 script の構文がハイライトされている。また、これ + らはソースとして実行できるため、実行して出力内容を確認できる。ソースと + して実行するには、`:'<,'>source` を使用する (|:source-range| を参照)。 + これは、|V| で行を視覚的に選択し、`:so` と入力することで実行できる。 + 例えば、以下の Vim9 script で試してみてほしい: >vim9 + + vim9script + echowindow "Welcome to Vim9 script!" +< + ソースコードとして提供すべきではないコード例もある。これらは、ソース + コードとして提供可能な例を必要としない概念を説明しているものである。こ + のようなコードブロックは、以下のように、汎用コード構文のハイライトで表 + 示される: > + + def ThisFunction() # スクリプトローカル + def g:ThatFunction() # グローバル + export def Function() # import および import の自動ロー + # ド用 + ============================================================================== 1. Vim9 script とは *Vim9-script* @@ -1902,84 +1924,192 @@ NOTE: ジョブなどの特殊な変数はデフォルトで null 値になり ジェネリック関数は、引数と戻り値の型チェックを維持しながら、同じ関数を異なる型 の引数で使用できる。これにより、型安全性とコードの再利用性が確保される。 + 宣言~ *generic-function-declaration* - *E1553* *E1554* *E1559* -ジェネリック関数の型パラメータは、関数名の直後に山括弧 "<" と ">" で宣言され -る。複数の型名はコンマで区切られる: > + *E1553* *E1554* +ジェネリック関数の型変数は、関数名の直後に山括弧 ("<"i と ">") で囲まれた型パ +ラメータとして宣言される。複数の型パラメータはコンマで区切られる: > - def[!] {funcname}<{type} [, {types}]>([arguments])[: {return-type}] - {function body} - enddef -< -これらの型パラメータは、関数のシグネチャと本体内の他の型と同じように使用でき -る。例: > - - def MyFunc(param1: T): T - var f: A - var x = param1 - return x - enddef + def[!] {funcname}<{type} [, {types}]>([arguments])[: {return-type}] + {function body} + enddef +< *generic-function-example* +これらの型パラメータは、他の型と同様に、関数シグネチャとその本体内で使用でき +る。以下の例では、2 つのリストを tuple のリストに結合している: >vim9 + + vim9script + def Zip(first: list, second: list): list> + const LEN: number = ([first->len(), second->len()])->min() + final result: list> = [] + for i in range(LEN) + result->add((first[i], second[i])) + endfor + return result + enddef + var n: list = [61, 62, 63] + var s: list = ['a', 'b', 'c'] + echo $"Zip example #1: {Zip(n, s)}" + echo $"Zip example #2: {Zip(s, n)}" < *type-variable-naming* *E1552* -型変数には大文字 1 文字を使用するのが慣例である (例: T、A、X)。ただし、より長 -い名前も使用できる。名前は必ず大文字で始まる必要がある。 + *type-parameter-naming* +前の例と同様に、名前には大文字を 1 文字だけ使用するのが慣例である (例えば、T、 +U、Aなど)。複数の文字で構成されている場合もあるが、名前は必ず大文字で始まる必 +要がある。この例では、"Ok" は有効だが、"n" は無効である: >vim9 + vim9script + def MyFail(): void + enddef + # E1552: Type variable name must start with an uppercase letter: n... +< *E1558* *E1560* 関数は、ジェネリック関数または通常の関数のいずれかとして宣言および使用する必要 -がある。両方としてはできない。 +がある。両方を同時に使用することはできない。以下の Vim9 script は、これらのエ +ラーを示している: >vim9 + vim9script + My1558() + # Vim(eval):E1558: Unknown generic function: My1558 +< >vim9 + vim9script + def My1560(): void + enddef + My1560() + # Vim(echo):E1560: Not a generic function: My1560 +< *E1561* -型変数名は、クラス名、型エイリアス、列挙名、関数名、他の型変数名など、他の定義 -済みの名前と競合してはならない。 +型パラメータ名は他の識別子と競合してはならない: >vim9 + + vim9script + def My1561(): D + enddef + # E1561: Duplicate type variable name: D + + vim9script + enum E + Yes, No + endenum + def My1041(): E + enddef + # E0141: Redefining script item "E" +< ジェネリック関数の呼び出し~ *generic-function-call* ジェネリック関数を呼び出すには、関数名と引数リストの間に "<" と ">" で具体的な 型を指定する: > - MyFunc>() + MyFunc>() +< + 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(): void + enddef + My1556() + # E1556: Too many types specified for generic function ... +< >vim9 + vim9script + def My1557(): void + enddef + My1557() + # E1557: Not enough types specified for generic function ... +< >vim9 + vim9script + def My1559(): T + enddef + My1559() + # Vim(eval):E1559: Type arguments missing for generic function ... < - *E1555* *E1556* *E1557* -ジェネリック関数を呼び出す際に指定する具体的な型の数は、関数内の型変数の数と一 -致する必要がある。空の型リストは許可されない。Vim9 の任意の型 (|vim9-types|) -は、ジェネリック関数内の具体的な型として使用できる。 +任意の 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(lst: list, default: U): any + return lst->len() > 0 ? lst[0] : default + enddef + echo FirstOrDefault(['B', 'C'], false) # echos B + echo FirstOrDefault([], 42) # echos 42 + enddef + Outer() +< -ジェネリック型における型変数の参照~ +型変数を型引数として使用する~ -ジェネリック型では、具体的な型の代わりに型変数を使用できる。これは、辞書のリス -トやリストの辞書のような複雑なデータ構造に便利である。例: > +型変数は型引数として渡すこともできる。例: >vim9 - vim9script + vim9script + # T は型パラメータとして宣言されている + # これは 'value' パラメータと戻り値の型に使用される + def Id(value: T): T + return value + enddef + # U は型パラメータとして宣言されている + # これは 'value' パラメータと戻り値の型に使用される + def CallId(value: U): U + # U は型引数として渡される/使用される型変数である + return Id(value) + enddef + echo CallId('I am') .. ' ' .. CallId(42) - def Flatten(x: list>): list - var result: list = [] - for inner in x - result += inner - endfor - return result - enddef +これは、リストの辞書や、以下の例のように辞書のリストのような複雑なデータ構造に +役立つ: >vim9 - echo Flatten([[1, 2], [3]]) + vim9script + def Flatten(x: list>): list + final result: list = [] + for inner in x + result->extend(inner) + endfor + return result + enddef + const ENGLISH: list> = [{1: 'one'}, {2: 'two'}] + const MANDARIN: list> = [{1: '壹'}, {2: '贰'}] + const ARABIC_N: list> = [{1: 1}, {2: 2}] + echo Flatten>([ENGLISH, MANDARIN]) + echo Flatten>([ENGLISH, ARABIC_N]) < +"Flatten" では、"T" は宣言された型パラメータである。関数内の他の場所では、 +"T" はその型パラメータを参照する型変数である。 + ジェネリッククラスメソッド~ -Vim9 クラスメソッドはジェネリック関数にすることができる: > +Vim9 クラスのメソッドはジェネリック関数になることができる: >vim9 - class A - def Foo() - enddef - endclass - var a = A.new() - a.Foo() + vim9script + class Config + var settings: dict + def Get(key: string): T + return this.settings[key] + enddef + endclass + var c: Config = Config.new({timeout: 30, debug: true}) + echo c.Get('timeout') + echo c.Get('debug') < *E1432* *E1433* *E1434* 基底クラスのジェネリッククラスメソッドは、子クラスのジェネリックメソッドでオー @@ -1990,33 +2120,38 @@ Vim9 クラスメソッドはジェネリック関数にすることができる ジェネリック関数参照~ 関数参照 (|Funcref|) はジェネリック関数として使用できる。これにより、特定の型 -を操作する関数のファクトリーを作成できる: > - - vim9script +を操作する関数のファクトリーを作成できる: >vim9 - def MakeEcho(): func(T): T - return (x: T): T => x - enddef - - var EchoNumber = MakeEcho() - echo EchoNumber(123) - - var EchoString = MakeEcho() - echo EchoString('abc') + vim9script + # 文字列内の指定された文字、またはリスト内の文字の 10 進数値と一致す + # る。Note: '*' は 10 進数の 42 (U+002A) + var c: string = "*" + var char_dec: tuple = (c, c->char2nr()->string()) + def Matcher(pattern: string): func(T): bool + return (value: T): bool => match(value, pattern) >= 0 + enddef + var StringMatch = Matcher(char_dec[0]) + echo "*+"->StringMatch() # true (has *) + echo ",-"->StringMatch() # false + var ListMatch = Matcher>(char_dec[1]) + echo [42, 43]->ListMatch() # true (has 42) + echo [44, 45]->ListMatch() # false < + ジェネリック関数のコンパイルと逆アセンブル~ |:defcompile| コマンドは、具体的な型の特定のリストを持つジェネリック関数をコン パイルするために使用できる: > - defcompile MyFunc, dict> + defcompile MyFunc, dict> < |:disassemble| コマンドを使用すると、ジェネリック関数に対して生成された命令を 一覧表示できる: > - disassemble MyFunc> - disassemble MyFunc> + disassemble MyFunc> + disassemble MyFunc> < + 制限事項と今後の課題~ 現在、Vim は以下をサポートしていない: @@ -2077,28 +2212,43 @@ Vim9 script では、グローバルな "g:" 名前空間は従来通り使用 りません。 *vim9-mix* -1つのスクリプトファイルで、旧来と Vim9 の両方の構文を使用する方法があります: > - " コメントはこちら +1 つのスクリプトファイルで、旧来と Vim9 の両方の構文を使用する方法がある: >vim9 + + " _legacy Vim script_ comments here if !has('vim9script') - " 旧来のスクリプトのコマンドはこちら + " _legacy Vim script_ comments/commands here finish endif vim9script - # Vim9 script のコマンドはこちら -これにより、可能であれば Vim9 script 構文を利用したスクリプトを書くことができ -ますが、Vim9 のないバージョンでも動作するようになります。 + # _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 をチェックする方 -が安全です。 - -これは、2通りの方法でしか機能しません: -1. "if" 文は false と評価され、`endif` までのコマンドはスキップされ、 - `vim9script` が実際に実行される最初のコマンドとなる。 -2. "if" 文が true に評価されると、`endif` までのコマンドが実行され、`finish` は - `vim9script` に到達する前に脱出します。 - +"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* @@ -2240,37 +2390,40 @@ Note これは変数には効かず、関数にしか効かないということ 起動速度を最適化するために、スクリプトの読み込みは実際に必要になるまで延期する 必要があります。オートロード機構を使用することをお勧めします: *E1264* -1. プラグインでは、オートロードスクリプトから import された項目を参照するユー - ザーコマンド、関数、マッピングを定義します。 > + 1. プラグインでは、オートロードスクリプトから import された項目を参照する + ユーザーコマンド、関数、マッピングを定義する。 > + import autoload 'for/search.vim' command -nargs=1 SearchForStuff search.Stuff() -< これは、.../plugin/anyname.vim に入ります。"anyname.vim" は自由に選ぶことが - できます。"SearchForStuff " コマンドが利用できるようになりました。 +< これは、.../plugin/anyname.vim に入る。"anyname.vim" は自由に選ぶこと + ができる。"SearchForStuff" コマンドが利用できるようになった。 - `import` の引数 "autoload" は、項目の1つが実際に使用されるまでスクリプトが - ロードされないことを意味します。スクリプトは "import" ディレクトリではなく、 - 'runtimepath' の "autoload" ディレクトリの下に見つかります。また、相対名や - 絶対名を使用することもできます。以下を参照。 + `import` の引数 "autoload" は、項目の1つが実際に使用されるまでスクリプ + トがロードされないことを意味する。スクリプトは "import" ディレクトリ + ではなく、'runtimepath' の "autoload" ディレクトリの下に見つかる。 + あるいは、相対名または絶対名のいずれかを使用することもできる。以下を参 + 照。 -2. オートロードスクリプトに、コードの大部分を置きます。 > + 2. オートロードスクリプトに、コードの大部分を置きます。 > vim9script - export def Stuff(arg: string) + export def Stuff(arg: string): void ... -< これは、.../autoload/for/search.vim に入ります。 +< これは、.../autoload/for/search.vim に入ります。 - "search.vim" スクリプトを "/autoload/for/" ディレクトリに置くと、export さ - れた項目に "for#search#" というプリフィックスが付くようになります。このプリ - フィックスは、旧来のオートロードスクリプトで手動で行うように、ファイル名か - ら取得されます。したがって、export された関数は "for#search#Stuff" で見つけ - ることができますが、通常は `import autoload` を使用し、プリフィックスを使用 - しません (この名前に遭遇した関数をコンパイルするときにオートロードスクリプ - トをロードするという副作用が発生します)。 + "search.vim" スクリプトを "/autoload/for/" ディレクトリに置くと、 + export された項目に "for#search#" というプリフィックスが付くようになり + ます。プリフィックスは、旧来のオートロードスクリプトで手動で追加するの + と同じように、ファイル名から取得される。したがって、export された関数 + は"for#search#Stuff" で見つけることができますが、通常は + `import autoload` を使用し、プリフィックスを使用しません (この名前に遭 + 遇した関数をコンパイルするときにオートロードスクリプトをロードするとい + う副作用が発生します)。 - 機能を分割して、オートロードスクリプトから他のスクリプトを好きなように - import することができます。こうすることで、プラグイン間でコードを共有するこ - とができます。 + 機能を分割して、オートロードスクリプトから他のスクリプトを好きなように + import することができます。こうすることで、プラグイン間でコードを共有 + することができます。 'runtimepath' のすべてのエントリからオートロードスクリプトを検索するのは、少し時 間がかかることがあります。プラグインがスクリプトの場所を知っている場合、多くの @@ -2314,14 +2467,14 @@ import したオートロードスクリプトを使用するマッピングを 7. クラスとインターフェイス *vim9-classes* -旧来のスクリプトでは、関数であるメンバーを追加することで、辞書を一種のオブジェ -クトとして使用することができます。しかし、これは非常に非効率的で、すべてのオブ -ジェクトが正しいメンバーを持っていることを確認する作業を作成者が行う必要があり -ます。|Dictionary-function| を参照。 +旧来の Vim script では、関数であるメンバーを追加することで、辞書を一種のオブ +ジェクトとして使用することができます。しかし、これは非常に非効率的で、すべての +オブジェクトが正しいメンバーを持っていることを確認する作業を作成者が行う必要が +あります。|Dictionary-function| を参照。 |Vim9| script では、一般的なオブジェクト指向プログラミング言語のように、クラス、 -オブジェクト、インターフェイスを持つことができます。これは多くの機能を含んでい -るため、別のヘルプファイルに記載されています: |vim9class.txt| +オブジェクト、インターフェイス、および列挙型を持つことができます。これは多くの +機能を含んでいるため、別のヘルプファイルに記載されています: |vim9class.txt| ============================================================================== @@ -2354,10 +2507,18 @@ Vim コマンドの行を命令にコンパイルする場合、可能な限り す。例えば、"+" 文字に遭遇し、これを一般的な加算命令にコンパイルする場合、実行 時に引数の型を検査し、どのような加算を行うかを決定する必要があります。そして、 その型が辞書であった場合には、エラーを投げます。もし、型が数値であることが分か -っていれば、「数値の加算」命令を使うことができ、その方が高速です。このエラーは -コンパイル時に発生する可能性があり、2 つの数値の加算は失敗しないため、実行時の -エラー処理は必要ありません。 +っていれば、「数値の加算」命令を使うことができ、その方が高速です。エラーはコン +パイル時に発生し、2 つの数値の加算が失敗することはほとんどないため、実行時にエ +ラー処理は必要ない。 + + NOTE: 余談だが、例外として整数オーバーフローがある。これは、結果が整数の最 + 大値を超える場合である。例えば、64 ビット符号付き整数に加算した結果が 2^63 + を超える場合を考える: >vim9 + vim9script + echo 9223372036854775807 + 1 # -9223372036854775808 + echo 2->pow(63)->float2nr() + 1 # -9223372036854775808 +< 複合型に を使った型の構文は、Javaと似ています。理解しやすく、広く使われ ています。型名は、以前 Vim で使われていたものに、"void" や "bool" などが追加さ れています。 @@ -2467,11 +2628,11 @@ script でも使われていたもので、意味はほぼ同じです。 def Func(arg1: number, arg2: string): bool 2つの選択肢を検討しました: -1. Dart のように名前の前に型を置く: > + 1. Dart のように名前の前に型を置く: > var list mylist final list mylist = ['foo'] def Func(number arg1, string arg2) bool -2. 変数名の後に型を入れるが、Go のようにコロンを使わない: > +< 2. 変数名の後に型を入れるが、Go のようにコロンを使わない: > var mylist list final mylist list = ['foo'] def Func(arg1 number, arg2 string) bool @@ -2508,7 +2669,14 @@ 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: `!!''` `!![]` `!!{}` @@ -2543,9 +2711,9 @@ Vim9 script では、JavaScript の import および export 機構に非常に い。 - スクリプトローカルにするための Vim 特有の "s:" の使用をやめることができる。 -(Vim9 または旧来の script から) Vim9 script を読み込む場合、グローバルに定義さ -れた項目のみが使用可能で、export された項目は使用できません。代替案を検討しま -した: +(Vim9 script または旧来の Vim script から) Vim9 script を読み込む場合、グロー +バルに定義された項目のみが使用可能で、export された項目は使用できません。代替 +案を検討しました: - export された項目は、すべてスクリプトローカル項目として利用できるようにする。 これにより、どの項目が定義されるかは制御できず、すぐにトラブルに発展する可能 性がある。 @@ -2596,11 +2764,11 @@ Vim は Perl、Python、Lua、Tcl、その他いくつかへのインターフ ことを推奨します。我々は、これをどうにかして簡単にできるように努力することがで きます。 -外部ツールの使用にはデメリットもあります。代替案としては、ツールを Vim script -に変換することです。この場合、過度の翻訳を行ずに、同時にコードを高速に保つに -は、ツールの構成要素がサポートされている必要があります。ほとんどの言語がクラス -をサポートしているので、Vim でクラスがサポートされていないのは問題です。 - +外部ツールの使用にもデメリットがある。代替案として、ツールを Vim Script に変換 +する方法がある。これを実現するには、余分な翻訳作業を避けつつコードの高速性を維 +持する必要があり、ツールの構造をサポートする必要がある。Vim9 Script ではクラ +ス、オブジェクト、インターフェイス、列挙型がサポートされているため、これはます +ます実現可能になっている。 vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/en/vim9.txt b/en/vim9.txt index 3de4a75e9..7941b83ab 100644 --- a/en/vim9.txt +++ b/en/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 9.1. Last change: 2025 Aug 27 +*vim9.txt* For Vim version 9.1. Last change: 2025 Oct 06 VIM REFERENCE MANUAL by Bram Moolenaar @@ -18,9 +18,29 @@ features in Vim9 script. 5. Generic functions |generic-functions| 6. Namespace, Import and Export |vim9script| 7. Classes and interfaces |vim9-classes| - 8. Rationale |vim9-rationale| + +------------------------------------------------------------------------------ + + NOTE: In this vim9.txt help file, the Vim9 script code blocks beginning + with `vim9script` are Vim9 script syntax highlighted. Also, they are + sourceable, meaning you can run them to see what they output. To + source them, use `:'<,'>source` (see |:source-range|), which is done + by visually selecting the line(s) with |V| and typing `:so`. + For example, try it on the following Vim9 script: >vim9 + + vim9script + echowindow "Welcome to Vim9 script!" +< + There are also code examples that should not be sourced - they + explain concepts that don't require a sourceable example. Such code + blocks appear in generic code syntax highlighting, like this: > + + def ThisFunction() # script-local + def g:ThatFunction() # global + export def Function() # for import and import autoload + ============================================================================== 1. What is Vim9 script? *Vim9-script* @@ -1903,126 +1923,240 @@ A generic function allows using the same function with different type arguments, while retaining type checking for arguments and the return value. This provides type safety and code reusability. + Declaration~ *generic-function-declaration* - *E1553* *E1554* *E1559* -The type parameters for a generic function are declared in angle brackets "<" -and ">" directly after the function name. Multiple type names are separated -by commas: > - - def[!] {funcname}<{type} [, {types}]>([arguments])[: {return-type}] - {function body} - enddef -< -These type parameters can then be used like any other type within the function -signature and body. Example: > - - def MyFunc(param1: T): T - var f: A - var x = param1 - return x - enddef + *E1553* *E1554* +The type variables for a generic function are declared as its type parameters +within angle brackets ("<" and ">"), directly after the function name. +Multiple type parameters are separated by commas: > + + def[!] {funcname}<{type} [, {types}]>([arguments])[: {return-type}] + {function body} + enddef +< *generic-function-example* +These type parameters may then be used, like any other type, within the +function signature and its body. The following example combines two lists +into a list of tuples: >vim9 + + vim9script + def Zip(first: list, second: list): list> + const LEN: number = ([first->len(), second->len()])->min() + final result: list> = [] + for i in range(LEN) + result->add((first[i], second[i])) + endfor + return result + enddef + var n: list = [61, 62, 63] + var s: list = ['a', 'b', 'c'] + echo $"Zip example #1: {Zip(n, s)}" + echo $"Zip example #2: {Zip(s, n)}" < *type-variable-naming* *E1552* -The convention is to use a single uppercase letter for a type variable (e.g., -T, A, X), although longer names are allowed. The name must start with an -uppercase letter. + *type-parameter-naming* +As in the preceding example, the convention is to use a single capital letter +for a name (e.g., T, U, A, etc.). Although they may comprise more than one +letter, names must start with a capital letter. In this example, "Ok" is +valid whereas "n" is not: >vim9 + vim9script + def MyFail(): void + enddef + # E1552: Type variable name must start with an uppercase letter: n... +< *E1558* *E1560* -A function must be declared and used either as generic or as a regular -function - but not both. +A function must be declared and used either as a generic function or as a +regular function, but not both. The following Vim9 scripts demonstrate these +errors: >vim9 + vim9script + My1558() + # Vim(eval):E1558: Unknown generic function: My1558 +< >vim9 + vim9script + def My1560(): void + enddef + My1560() + # Vim(echo):E1560: Not a generic function: My1560 +< *E1561* -A type variable name must not conflict with other defined names, such as class -names, type aliases, enum names, function names or other type variable names. +Type parameter names must not clash with other identifiers: >vim9 + + vim9script + def My1561(): D + enddef + # E1561: Duplicate type variable name: D + + vim9script + enum E + Yes, No + endenum + def My1041(): E + enddef + # E0141: Redefining script item "E" +< Calling a generic function~ *generic-function-call* To call a generic function, specify the concrete types in "<" and ">" between the function name and the argument list: > - MyFunc>() + MyFunc>() +< + NOTE: There are several working examples in this section, which may + be sourced, including |generic-function-example|. + + *E1555* *E1556* *E1557* *E1559* +The number of passed type arguments to the function must match the number +of its declared type parameters. An empty type list is not allowed. +Examples: >vim9 + + vim9script + def My1555<>(): void + enddef + # E1555: Empty type list specified for generic function ... +< >vim9 + vim9script + def My1556(): void + enddef + My1556() + # E1556: Too many types specified for generic function ... +< >vim9 + vim9script + def My1557(): void + enddef + My1557() + # E1557: Not enough types specified for generic function ... +< >vim9 + vim9script + def My1559(): T + enddef + My1559() + # Vim(eval):E1559: Type arguments missing for generic function ... < - *E1555* *E1556* *E1557* -The number of concrete types provided when calling a generic function must -match the number of type variables in the function. An empty type list is not -allowed. Any Vim9 type (|vim9-types|) can be used as a concrete type in a -generic function. +Any Vim9 type (|vim9-types|) can be used as a concrete type in a generic +function. -Spaces are not allowed between the function name and "<", or between ">" and -the opening "(". +Spaces are not allowed: +- Between the function name and "<" (|E1068|) +- Between ">" and the opening "(" (|E1068|), or +- Within the "<" and ">", except where required after the comma separating + the types (|E1202|). A generic function can be exported and imported like a regular function. See |:export| and |:import|. A generic function can be defined inside another regular or generic function. +Example: >vim9 + vim9script + def Outer(): void + # Returns either the first item of a list or a default value + def FirstOrDefault(lst: list, default: U): any + return lst->len() > 0 ? lst[0] : default + enddef + echo FirstOrDefault(['B', 'C'], false) # echos B + echo FirstOrDefault([], 42) # echos 42 + enddef + Outer() +< -Referencing type variables in generic types~ +Using a type variable as a type argument ~ -Instead of concrete types, type variables can be used with generic types. -This is useful for complex data structures like lists of dictionaries or -dictionaries of lists. Example: > +A type variable may also be passed as a type argument. For example: >vim9 - vim9script + vim9script + # T is declared as a type parameter + # It is used for the 'value' parameter and the return type + def Id(value: T): T + return value + enddef + # U is declared as a type parameter + # It is used for the 'value' parameter and the return type + def CallId(value: U): U + # U is a type variable passed/used as a type argument + return Id(value) + enddef + echo CallId('I am') .. ' ' .. CallId(42) - def Flatten(x: list>): list - var result: list = [] - for inner in x - result += inner - endfor - return result - enddef +This is useful for complex data structures like dictionaries of lists or, +as in the following example, lists of dictionaries: >vim9 - echo Flatten([[1, 2], [3]]) + vim9script + def Flatten(x: list>): list + final result: list = [] + for inner in x + result->extend(inner) + endfor + return result + enddef + const ENGLISH: list> = [{1: 'one'}, {2: 'two'}] + const MANDARIN: list> = [{1: '壹'}, {2: '贰'}] + const ARABIC_N: list> = [{1: 1}, {2: 2}] + echo Flatten>([ENGLISH, MANDARIN]) + echo Flatten>([ENGLISH, ARABIC_N]) < +In "Flatten", "T" is a declared type parameter. Everywhere else in +the function, "T" is a type variable referencing that type parameter. + Generic class method~ -A Vim9 class method can be a generic function: > +A Vim9 class method can be a generic function: >vim9 - class A - def Foo() - enddef - endclass - var a = A.new() - a.Foo() + vim9script + class Config + var settings: dict + def Get(key: string): T + return this.settings[key] + enddef + endclass + var c: Config = Config.new({timeout: 30, debug: true}) + echo c.Get('timeout') + echo c.Get('debug') < *E1432* *E1433* *E1434* A generic class method in a base class can be overridden by a generic method -in a child class. The number of type variables must match between both +in a child class. The number of type variables must match between both methods. A concrete class method cannot be overridden by a generic method, and vice versa. + Generic function reference~ A function reference (|Funcref|) can be a generic function. This allows for -creating factories of functions that operate on specific types: > - - vim9script - - def MakeEcho(): func(T): T - return (x: T): T => x - enddef - - var EchoNumber = MakeEcho() - echo EchoNumber(123) +creating factories of functions that operate on specific types: >vim9 - var EchoString = MakeEcho() - echo EchoString('abc') + vim9script + # Match a specified character in a string or the decimal value of the + # character in a list. Note: '*' is decimal 42 (U+002A) + var c: string = "*" + var char_dec: tuple = (c, c->char2nr()->string()) + def Matcher(pattern: string): func(T): bool + return (value: T): bool => match(value, pattern) >= 0 + enddef + var StringMatch = Matcher(char_dec[0]) + echo "*+"->StringMatch() # true (has *) + echo ",-"->StringMatch() # false + var ListMatch = Matcher>(char_dec[1]) + echo [42, 43]->ListMatch() # true (has 42) + echo [44, 45]->ListMatch() # false < + Compiling and Disassembling Generic functions~ The |:defcompile| command can be used to compile a generic function with a specific list of concrete types: > - defcompile MyFunc, dict> + defcompile MyFunc, dict> < The |:disassemble| command can be used to list the instructions generated for a generic function: > - disassemble MyFunc> - disassemble MyFunc> + disassemble MyFunc> + disassemble MyFunc> < + Limitations and Future Work~ Currently, Vim does not support: @@ -2083,26 +2217,41 @@ original value to get the same effect. The order of flags may change. In the |vimrc| file sourced on startup this does not happen. *vim9-mix* -There is one way to use both legacy and Vim9 syntax in one script file: > - " comments may go here +There is one way to use both legacy and Vim9 syntax in one script file: >vim9 + + " _legacy Vim script_ comments here if !has('vim9script') - " legacy script commands go here + " _legacy Vim script_ comments/commands here finish endif vim9script - # Vim9 script commands go here + # _Vim9 script_ commands/commands from here onwards + echowindow $"has('vim9script') == {has('vim9script')}" +< This allows for writing a script that takes advantage of the Vim9 script -syntax if possible, but will also work on a Vim version without it. +syntax if possible, and prevents the vim9script command from throwing an +error if used in a version of Vim without 'vim9script'. Note that Vim9 syntax changed before Vim 9 so that scripts using the current syntax (such as "import from" instead of "import") might throw errors. -To prevent these, a safer check could be for |v:version| >= 900 instead. - -This can only work in two ways: -1. The "if" statement evaluates to false, the commands up to `endif` are - skipped and `vim9script` is then the first command actually executed. -2. The "if" statement evaluates to true, the commands up to `endif` are - executed and `finish` bails out before reaching `vim9script`. +To prevent these, a safer check may be |v:version| >= 900 instead (because +"has('vim9script')" will return `v:true` back to Vim 8.2 with patch 3965). +Sometimes it is prudent to cut off even later. Vim9 script's feature set +continues to grow so, for example, if tuples are used (introduced in Vim 9.1 +patch 1232), a better condition is: >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 ..." +< +Whichever vim-mix condition is used, it only works in one of two ways: + 1. The "if" statement evaluates to false, the commands up to `endif` are + skipped and `vim9script` is then the first command actually executed. + 2. The "if" statement evaluates to true, the commands up to `endif` are + executed and `finish` bails out before reaching `vim9script`. Export ~ @@ -2127,13 +2276,13 @@ interfaces and enums can be exported. Import ~ *:import* *:imp* *E1094* *E1047* *E1262* *E1048* *E1049* *E1053* *E1071* *E1088* *E1236* -The exported items can be imported in another script. The import syntax has -two forms. The simple form: > +The exported items can be imported in another script. The import syntax has +two forms. The simple form: > import {filename} < Where {filename} is an expression that must evaluate to a string. In this form the filename should end in ".vim" and the portion before ".vim" will -become the script local name of the namespace. For example: > +become the script local name of the namespace. For example: > import "myscript.vim" < This makes each exported item in "myscript.vim" available as "myscript.item". @@ -2209,7 +2358,7 @@ Note that this does not work for variables, only for functions. *import-legacy* *legacy-import* `:import` can also be used in legacy Vim script. The imported namespace still -becomes script-local, even when the "s:" prefix is not given. For example: > +becomes script-local, even when the "s:" prefix is not given. For example: > import "myfile.vim" call s:myfile.MyFunc() @@ -2224,8 +2373,8 @@ However, the namespace cannot be resolved on its own: > < This also affects the use of || in the legacy mapping context. Since || is only a valid prefix for a function and NOT for a namespace, you -cannot use it to scope a function in a script local namespace. Instead of -prefixing the function with || you should use||. For example: +cannot use it to scope a function in a script local namespace. Instead of +prefixing the function with || you should use ||. For example: > noremap ,a :call s:that.OtherFunc() < @@ -2243,43 +2392,47 @@ Importing an autoload script ~ For optimal startup speed, loading scripts should be postponed until they are actually needed. Using the autoload mechanism is recommended: *E1264* -1. In the plugin define user commands, functions and/or mappings that refer to - items imported from an autoload script. > + 1. In the plugin, define user commands, functions and/or mappings + referring to items imported from an autoload script. > + import autoload 'for/search.vim' command -nargs=1 SearchForStuff search.Stuff() -< This goes in .../plugin/anyname.vim. "anyname.vim" can be freely chosen. - The "SearchForStuff" command is now available to the user. +< This goes in .../plugin/anyname.vim. "anyname.vim" can be freely + chosen. The "SearchForStuff" command is now available to the user. - The "autoload" argument to `:import` means that the script is not loaded - until one of the items is actually used. The script will be found under - the "autoload" directory in 'runtimepath' instead of the "import" - directory. Alternatively a relative or absolute name can be used, see - below. + The "autoload" argument to `:import` means that the script is not + loaded until one of the items is actually used. The script will be + found under the "autoload" directory in 'runtimepath' instead of the + "import" directory. Alternatively, either a relative or absolute + name can be used - see below. + + 2. In the autoload script put the bulk of the code. > -2. In the autoload script put the bulk of the code. > vim9script - export def Stuff(arg: string) + export def Stuff(arg: string): void ... -< This goes in .../autoload/for/search.vim. +< This goes in .../autoload/for/search.vim. - Putting the "search.vim" script under the "/autoload/for/" directory has - the effect that "for#search#" will be prefixed to every exported item. The - prefix is obtained from the file name, as you would to manually in a - legacy autoload script. Thus the exported function can be found with - "for#search#Stuff", but you would normally use `import autoload` and not - use the prefix (which has the side effect of loading the autoload script - when compiling a function that encounters this name). + Putting the "search.vim" script under the "/autoload/for/" directory + has the effect that "for#search#" will be prefixed to every exported + item. The prefix is obtained from the file name, just as you would + add it manually in a legacy autoload script. Thus the exported + function can be found with "for#search#Stuff", but you would normally + use `import autoload` and not use the prefix (which has the side effect + of loading the autoload script when compiling a function that + encounters this name). - You can split up the functionality and import other scripts from the - autoload script as you like. This way you can share code between plugins. + You can split up the functionality and import other scripts from the + autoload script as you like. This way you can share code between + plugins. Searching for the autoload script in all entries in 'runtimepath' can be a bit slow. If the plugin knows where the script is located, quite often a relative path can be used. This avoids the search and should be quite a bit faster. -Another advantage is that the script name does not need to be unique. An -absolute path is also possible. Examples: > +Another advantage is that the script name does not need to be unique. Also, +an absolute path is possible. Examples: > import autoload '../lib/implement.vim' import autoload MyScriptsDir .. '/lib/implement.vim' @@ -2316,14 +2469,14 @@ Or: > 7. Classes and interfaces *vim9-classes* -In legacy script a Dictionary could be used as a kind-of object, by adding +In legacy Vim script, a Dictionary could be used as a kind-of object by adding members that are functions. However, this is quite inefficient and requires the writer to do the work of making sure all the objects have the right members. See |Dictionary-function|. -In |Vim9| script you can have classes, objects and interfaces like in most -popular object-oriented programming languages. Since this is a lot of -functionality it is located in a separate help file: |vim9class.txt|. +In |Vim9| script you can have classes, objects, interfaces, and enums like +in most popular object-oriented programming languages. Since this is a lot +of functionality, it is located in a separate help file: |vim9class.txt|. ============================================================================== @@ -2345,7 +2498,7 @@ which allows for a function with different semantics. Most things still work as before, but some parts do not. A new way to define a function was considered the best way to separate the legacy style code from Vim9 style code. -Using "def" to define a function comes from Python. Other languages use +Using "def" to define a function comes from Python. Other languages use "function" which clashes with legacy Vim script. @@ -2360,11 +2513,19 @@ arguments and decide what kind of addition to do. And when the type is dictionary throw an error. If the types are known to be numbers then an "add number" instruction can be used, which is faster. The error can be given at compile time, no error handling is needed at runtime, since adding two numbers -cannot fail. +almost never fails. -The syntax for types, using for compound types, is similar to Java. It -is easy to understand and widely used. The type names are what were used in -Vim before, with some additions such as "void" and "bool". + NOTE: As a tangential point, the exception is integer overflow, where the + result exceeds the maximum integer value. For example, adding to a 64-bit + signed integer where the result is greater than 2^63: >vim9 + + vim9script + echo 9223372036854775807 + 1 # -9223372036854775808 + echo 2->pow(63)->float2nr() + 1 # -9223372036854775808 +< +The syntax for types, using for compound types, is similar to Java. +It is easy to understand and widely used. The type names are what were used +in Vim before, with some additions such as "void" and "bool". Removing clutter and weirdness ~ @@ -2475,11 +2636,11 @@ This is how we put types in a declaration: > def Func(arg1: number, arg2: string): bool Two alternatives were considered: -1. Put the type before the name, like Dart: > + 1. Put the type before the name, like Dart: > var list mylist final list mylist = ['foo'] def Func(number arg1, string arg2) bool -2. Put the type after the variable name, but do not use a colon, like Go: > +< 2. Put the type after the variable name, but do not use a colon, like Go: > var mylist list final mylist list = ['foo'] def Func(arg1 number, arg2 string) bool @@ -2519,17 +2680,21 @@ functions return these values, and changing that causes more problems than it solves. After using this for a while it turned out to work well. If you have any type of value and want to use it as a boolean, use the `!!` -operator: - true: `!!'text'` `!![99]` `!!{'x': 1}` `!!99` - false: `!!''` `!![]` `!!{}` +operator (see |expr-!|): >vim9 + vim9script + # The following are all true: + echo [!!'text', !![1], !!{'x': 1}, !!1, !!1.1] + # And these are all false: + echo [!!'', !![], !!{}, !!0, !!0.0] +< From a language like JavaScript we have this handy construct: > GetName() || 'unknown' However, this conflicts with only allowing a boolean for a condition. Therefore the "??" operator was added: > GetName() ?? 'unknown' Here you can explicitly express your intention to use the value as-is and not -result in a boolean. This is called the |falsy-operator|. +result in a boolean. This is called the |falsy-operator|. Import and Export ~ @@ -2554,9 +2719,9 @@ that works like one would expect: not needed. - The Vim-specific use of "s:" to make things script-local can be dropped. -When sourcing a Vim9 script (from a Vim9 or legacy script), only the items -defined globally can be used, not the exported items. Alternatives -considered: +When sourcing a Vim9 script (from either a Vim9 script or legacy Vim script), +only the items defined globally can be used, not the exported items. +Alternatives considered: - All the exported items become available as script-local items. This makes it uncontrollable what items get defined and likely soon leads to trouble. - Use the exported items and make them global. Disadvantage is that it's then @@ -2607,8 +2772,8 @@ and channels. We can try to make this easier somehow. Using an external tool also has disadvantages. An alternative is to convert the tool into Vim script. For that to be possible without too much translation, and keeping the code fast at the same time, the constructs of the -tool need to be supported. Since most languages support classes the lack of -support for classes in Vim is then a problem. +tool need to be supported. Since Vim9 script now includes support for +classes, objects, interfaces, and enums, that is increasingly feasible.