Movatterモバイル変換


[0]ホーム

URL:


Quick links:help overview ·quick reference ·user manual toc ·reference manual toc·faq
Go to keyword (shortcut:k)
Site search (shortcut:s)
vim9.txt  ForVim version 9.2.  Last change: 2026 Feb 14VIM REFERENCE MANUAL  by Bram MoolenaarVim9script commands and expressions.Vim9vim9Mostexpressionhelpis ineval.txt.  This fileis about the newsyntax andfeatures inVim9 script.1.  WhatisVim9 script?Vim9-script2.  Differencesvim9-differences3.  New stylefunctionsfast-functions4.  Typesvim9-types5.  Genericfunctionsgeneric-functions6.  Namespace, Import and Exportvim9script7.  Classes and interfacesvim9-classes8.  Rationalevim9-rationale------------------------------------------------------------------------------NOTE: In thisvim9.txthelp file, theVim9script code blocks beginningwithvim9script (and individual linesstarting withvim9cmd) areVim9scriptsyntax highlighted.  Also, they are sourceable, meaningyou can run them to see what they output.  To source them, use:'<,'>source (see:source-range), whichis done by visuallyselecting the line(s) withV and typing:so.  For example, tryiton the followingVim9 script:vim9scriptechowindow "Welcome to Vim9 script!"There are also code examples that should not be sourced- theyexplain concepts that don't requirea sourceable example.  Such codeblocks appear in generic codesyntax highlighting, like this:def ThisFunction()          # script-localdef g:ThatFunction()        # globalexport def Function()       # for import and import autoload==============================================================================1. WhatisVim9 script?Vim9-scriptVimscript has been growing over time, while preserving backwardscompatibility.  That means bad choices from the past often can't be changedand compatibility withVi restricts possible solutions.  Executionis quiteslow, each lineis parsed every timeitis executed.The main goal ofVim9scriptis to drastically improve performance.  Thisisaccomplished by compiling commands into instructions that can be efficientlyexecuted.  An increase in execution speed of 10 to 100 times can be expected.A secondary goalis to avoid Vim-specific constructs and get closer tocommonly used programming languages, suchas JavaScript, TypeScript and Java.The performance improvements can only be achieved by not being 100% backwardscompatible.  For example, making function arguments available in the "a:"dictionary adds quitea lot of overhead.  InaVim9 function this dictionaryis not available.  Other differences are more subtle, suchas howerrors arehandled.Vim9script syntax, semantics, and behavior apply in:-a function defined with the:def command-ascript file where the first commandisvim9script- anautocommand defined in the context of the above-a command prefixed with thevim9cmd command modifierWhen using:function inaVim9script file the legacysyntaxis used, withthe highestscriptversion.  However, this can be confusing andis thereforediscouraged.Vim9script and legacy Vimscript can be mixed.  Thereis no requirement torewrite old scripts, they keep workingas before.  You may want to usea few:deffunctions for code that needs to be fast.:vim9[cmd]{cmd}:vim9:vim9cmdEvaluate and execute{cmd} usingVim9script syntax,semantics, and behavior.  Useful when typinga command,ina:function, ora legacy Vim script.The following short example shows howa legacy Vimscriptcommand anda:vim9cmd (soVim9script context) may appearsimilar, though may differ not just syntactically, but alsosemantically and behaviorally.  call popup_notification('entrée'[5:]    \ ->str2list()->string(), #{time: 7000})  vim9cmd popup_notification('entrée'[5 :]      ->str2list()->string(), {time: 7000})Notes: 1) The reason for the different outputisVim9script   uses character indexing whereas legacy Vimscript   uses byte indexing- seevim9-string-index.2)Syntaxis different too.  InVim9 script:- Thespace in "[5 :]"is mandatory (seevim9-white-space).- Line continuation with "\"is not required.- The "#" (to avoid puttingquotes around dictionary    keys)is neither required nor allowed- see#{}.E1164:vim9cmd cannot stand alone;itmust be followed bya command.:leg[acy]{cmd}:leg:legacyEvaluate and execute{cmd} using legacy Vimscript syntax,semantics, and behavior.  Itis only applicable inaVim9script ora:def function.  Using an equivalentscript tothe one, above (see its notes for why the output differs):  vim9script  # Legacy context - so, this creates a popup with [769, 101]  legacy call popup_notification('entrée'[5:]    \ ->str2list()->string(), #{time: 7000})  # Vim9 script context - so, this creates a pop up with [101]  popup_notification('entrée'[5 :]      ->str2list()->string(), {time: 7000})Vim9scriptscript-localvariables may be used by prefixing"s:", like in legacy Vim script.  This example shows thedifference in syntax: "k" for thescript-local variable inVim9 script, "s:k" in the legacy Vimscript context.  vim9script  var k: string = "Okay"  echo k  legacy echo s:kE1189Using:legacyis not allowed in compiledVim9scriptcontrol flow contexts. For example:  vim9script  def F_1189()    if v:version == 900    # E1189: Cannot use :legacy with this command: endif    legacy endif  enddef  F_1189()E1234:legacy cannot stand alone;itmust be followed bya command.==============================================================================2. Differences from legacy Vimscriptvim9-differencesOverviewE1146Brief summary of the differences you will most often encounter when usingVim9script and:def functions; details are below:- Comments start with #, not ":echo "hello"   # comment- Usingabackslash for line continuationis hardly ever needed:echo "hello "     .. yourName     .. ", how are you?"- Whitespaceis required in many places to improve readability,  seevim9-white-space.- Assign values without:letE1126, declarevariables with:var:var count = 0count += 3- Constants can be declared with:final and:const:final matches = []  # add to the list laterconst names = ['Betty', 'Peter']  # cannot be changed-:final cannot be usedas an abbreviation of:finally.- Variables andfunctions arescript-local by default.- Functions are declared with argument types and return type:def CallMe(count: number, message: string): bool- Callfunctions without:call:writefile(['done'], 'file.txt')- You cannot use oldEx commands::Print:append:change:d  directly followed by 'd' or 'p'.:insert:k:mode:open:s  with only flags:t:xit- Some commands, especially those used for flow control, cannot be shortened.  E.g.,:throw cannot be writtenas:th.vim9-no-shorten- You cannot use curly-braces names.-A range beforea commandmust be prefixed witha colon::%s/this/that- Executinga register with "@r" does not work, you can prependa colon or use:exe::exe @a- Unless mentioned specifically, the highestscriptversionis used.- When defining anexpression mapping, theexpression will be evaluated in the  context of thescript whereit was defined.- When indexingastring theindexis counted in characters, not bytes:vim9-string-index- Some possibly unexpected differences:vim9-gotchas.Comments starting with #In legacy Vimscript comments start with doublequote.  InVim9scriptcomments start with #.# declarationsvar count = 0  # number of occurrencesThe reasonis thata doublequote can also be the start ofa string. In manyplaces, especially halfway through anexpression witha line break, it's hardto tell what the meaning is, since bothastring anda comment can be followedby arbitrary text.  To avoid confusion only# comments are recognized.  Thisis the sameas in shell scripts andPython programs.InVi#isa command tolist text with numbers.  InVim9script you can use:number for that.:101 numberTo improve readability theremust beaspace betweena command and the#that startsa comment:var name = value # commentvar name = value# error!E1170Do not starta comment with #{,it looks like the legacy dictionary literaland produces an error where this might be confusing.  #{{ or #{{{ are OK,these can be used to starta fold.Whenstarting to readascript file Vim doesn't knowitisVim9script untilthevim9script commandis found.  Until that point you would need to uselegacy comments:" legacy commentvim9script# Vim9 commentThat looks ugly, betterputvim9script in the very first line:vim9script# Vim9 commentIn legacy Vimscript#is also used for the alternate file name.  InVim9script you need to use %% instead.  Instead of ## use %%% (stands for allarguments).Vim9 functionsE1099A function defined with:defis compiled.  Executionis many times faster,often 10 to 100 times.Manyerrors are already found when compiling, before the functionis executed.Thesyntaxis strict, to enforce code thatiseasy to read and understand.Compilationis done when any of theseis encountered:- the first time the functionis called- when the:defcompile commandis encountered in thescript after the  function was defined-:disassembleis used for the function.-a function thatis compiled calls the function or usesitasa functionreference (so that the argument and return types can be checked)E1091E1191If compilation failsitis not tried again on the next call, instead thiserroris given: "E1091: Functionis not compiled:{name}".Compilation will fail when encounteringa user command that has not beencreated yet.  In thiscase you can callexecute() to invokeitat runtime.def MyFunc()  execute('DefinedLater')enddef:def has nooptions like:function does: "range", "abort", "dict" or"closure".A:def function always aborts on an error (unless:silent! wasused for the command or the error was caughta:try block), does not getarange passed, cannot bea "dict" function, and can always bea closure.vim9-no-dict-functionE1182You can useaVim9Class(Vim9-class) instead ofa "dict function".You can also pass the dictionary explicitly:def DictFunc(self: dict<any>, arg: string)   echo self[arg]enddefvar ad = {item: 'value', func: DictFunc}ad.func(ad, 'item')You can calla legacydict function though:func Legacy() dict  echo self.valueendfuncdef CallLegacy()  var d = {func: Legacy, value: 'text'}  d.func()enddefThe argument types and return type need to be specified.  The "any" type canbe used, type checking will then be doneat runtime, like with legacyfunctions.E1106Arguments are accessed by name, without "a:", just like any other language.Thereis no "a:" dictionary or "a:000" list.vim9-variable-argumentsE1055E1160E1180Variable arguments are definedas the last argument, witha name and havealist type, similar to TypeScript.  For example,alist of numbers:def MyFunc(...itemlist: list<number>)   for item in itemlist     ...Whena function argumentis optional (it hasa default value) passingv:noneas the argument results in using the default value.  Thisis useful when youwant to specifya value for an argument that comes after an argument thatshould use its default value.  Example:def MyFunc(one = 'one', last = 'last')  ...enddefMyFunc(v:none, 'LAST')  # first argument uses default value 'one'vim9-ignored-argumentE1181The argument "_" (an underscore) can be used to ignore the argument.  Thisismost useful in callbacks where you don't need it, butdo need to give anargument to match the call.  E.g. when usingmap() two arguments are passed,the key and the value, to ignore the key:map(numberList, (_, v) => v * 2)Thereis no error for using the "_" argument multiple times.  No type needs tobe given.Functions and variables are script-local by defaultvim9-scopesWhen using:function or:def to specifya new functionat thescript levelinaVim9 script, the functionis local to the script.  Like prefixing "s:" inlegacy script.  To definea global function or variable the "g:" prefixmustbe used.  Forfunctions inascript thatis to be imported and in anautoloadscript "export" needs to be used for those to be used elsewhere.def ThisFunction()          # script-localdef g:ThatFunction()        # globalexport def Function()       # for import and import autoloadE1075When using:function or:def to specifya nested function insidea:deffunction and no namespace was given, this nested functionis local to the codeblockitis defined in.  It cannot be used infunction() withastringargument, pass the functionreference itself:def Outer()  def Inner()    echo 'inner'  enddef  var Fok = function(Inner)     # OK  var Fbad = function('Inner')  # does not workDetail: thisis because "Inner" will actually becomea functionreference toafunction witha generated name.Itis not possible to defineascript-local function ina function.  You candefinea local function and assignit toascript-localFuncref (itmust havebeen declaredat thescript level).  Itis possible to definea globalfunction by using the "g:" prefix.When referring toa function and no "s:" or "g:" prefixis used, Vim willsearch for the function:- in the function scope, in block scopes- in thescript scopeImportedfunctions are found with the prefix from the:import command.Sinceascript-local functionreference can be used without "s:" the namemuststart with an uppercaseletter even when using the "s:" prefix.  In legacyscript "s:funcref" could be used, becauseit could not be referred to with"funcref".  InVim9scriptit can, therefore "s:Funcref"must be used to avoidthat the name interferes with builtin functions.vim9-s-namespaceE1268The use of the "s:" prefixis not supportedat theVim9script level.  Allfunctions andvariables withouta prefix are script-local.In:deffunctions the use of "s:" depends on the script: Script-localvariables andfunctions ina legacyscriptdo use "s:", while inaVim9scripttheydo not use "s:".  This matches what you see in the rest of the file.In legacyfunctions the use of "s:" forscript itemsis required,as before.No matter if thescriptisVim9 or legacy.In all cases the functionmust be defined before used.  Thatis whenitiscalled, when:defcompile causesit to be compiled, or when code that callsitis being compiled (to figure out the return type).The resultis thatfunctions andvariables withouta namespace can usually befound in the script, either defined there or imported.  Globalfunctions andvariables could be defined anywhere (good luck finding out where!  You canoften see whereit was last set using:verbose).E1102Globalfunctions can still be defined and deletedat nearly any time.  InVim9scriptscript-localfunctions are defined once when thescriptis sourcedand cannot be deleted or replaced by itself (it can be by reloading thescript).When compilinga function anda function callis encountered fora functionthatis not (yet) defined, theFuncUndefinedautocommandis not triggered.You can use anautoload function if needed, or calla legacy function and haveFuncUndefined triggered there.Reloading a Vim9 script clears functions and variables by defaultvim9-reloadE1149E1150When loadinga legacy Vimscripta second time nothingis removed, thecommands will replace existingvariables and functions, create new ones, andleave removed things hanging around.When loadingaVim9scripta second time all existingscript-localfunctionsandvariables are deleted, thus you start witha clean slate.  Thisis usefulif you are developingaplugin and want to trya new version.  If you renamedsomething you don't have to worry about the old name still hanging around.If youdo want to keep items, use:vim9script noclearYou want to use this in scripts that useafinish command to bail outatsome point when loaded again.  E.g. whena buffer local optionis set toafunction, the function does not need to be defined more than once:vim9script noclearsetlocal completefunc=SomeFuncif exists('*SomeFunc')  finishendifdef SomeFunc()....Variable declarations with :var, :final and :constvim9-declaration:varE1079E1017E1020E1054E1087E1124Localvariables need to be declared with:var.  Local constants need to bedeclared with:final or:const.  We refer to bothas "variables" in thissection.Variables can be local toa script, function or code block:vim9scriptvar script_var = 123def SomeFunc()  var func_var = script_var  if cond    var block_var = func_var  ...Thevariables are only visible in the block where they are defined and nestedblocks.  Once the block ends the variableis no longer accessible:if cond   var inner = 5else   var inner = 0endifecho inner  # Error!The declarationmust be done earlier:var inner: numberif cond   inner = 5else   inner = 0endifecho innerAlthough thisis shorter and faster for simple values:var inner = 0if cond   inner = 5endifecho innerE1025E1128To intentionally hidea variable from code that follows,a block can beused:{   var temp = 'temp'   ...}echo temp  # Error!Thisis especially useful ina user command:command -range Rename { var save = @a @a = 'some expression' echo 'do something with ' .. @a @a = save    }And with autocommands:   au BufWritePre *.go { var save = winsaveview() silent! exe ':%! some formatting command' winrestview(save)   }Although usinga:def function probably works better.E1022E1103E1130E1131E1133E1134Declaringa variable witha type but without an initializer will initialize tofalse (for bool), empty (for string, list, dict, etc.) or zero (for number,any, etc.).  This matters especially when using the "any" type, the value willdefault to the number zero.  For example, when declaringa list, items can beadded:var myList: list<number>myList->add(7)Initializinga variable toanull value, e.g.null_list, differs from notinitializing the variable.  This throws an error:var myList = null_listmyList->add(7)  # E1130: Cannot add to null listE1016E1052E1066InVim9script:let cannot be used.  An existing variableis assigned towithout any command.  The same for global, window, tab, buffer and Vimvariables, because they are not really declared.  Those can also be deletedwith:unlet.E1065You cannot use:va to declarea variable,itmust be written with the fullname:var.  Just to make sureitiseasy to read.E1178:lockvar does not work on local variables.  Use:const and:finalinstead.Theexists() andexists_compiled()functionsdo not work on localvariablesor arguments.E1006E1041E1167E1168E1213Variables,functions and function arguments cannot shadow previously definedor importedvariables andfunctions in the samescript file.Variables may shadowEx commands, rename the variable if needed.Globalvariablesmust be prefixed with "g:", alsoat thescript level.vim9scriptvar script_local = 'text'g:global = 'value'var Funcref = g:ThatFunctionGlobalfunctionsmust be prefixed with "g:":vim9scriptdef g:GlobalFunc(): string  return 'text'enddefecho g:GlobalFunc()The "g:" prefixis not needed for auto-load functions.vim9-function-defined-laterAlthough globalfunctions can be called without the "g:" prefix, theymustexist when compiled.  By adding the "g:" prefix the function can be definedlater.  Example:def CallPluginFunc()  if exists('g:loaded_plugin')    g:PluginFunc()  endifenddefIf youdoit like this, you get an errorat compile time that "PluginFunc"does not exist, even when "g:loaded_plugin" does not exist:def CallPluginFunc()  if exists('g:loaded_plugin')    PluginFunc()   # Error - function not found  endifenddefYou can useexists_compiled() to avoid the error, but then the function wouldnot be called, even when "g:loaded_plugin"is defined later:def CallPluginFunc()  if exists_compiled('g:loaded_plugin')    PluginFunc()   # Function may never be called  endifenddefSince `&opt= value`is now assigninga value to option "opt", ":&" cannot beused to repeata:substitute command.vim9-unpack-ignoreFor an unpack assignment the underscore can be used to ignorealist item,similar to howa function argument can be ignored:[a, _, c] = theListTo ignore any remaining items:[a, b; _] = longListE1163E1080Declaring more than one variableata time, using the unpack notation,ispossible.  Each variable can havea type or inferit from the value:var [v1: number, v2] = GetValues()Use this only when thereisalist with values, declaring one variable perlineis much easier to read and change later.Constantsvim9-constvim9-finalHow constants work varies between languages.  Some considera variable thatcan't be assigned another valuea constant.  JavaScriptis an example.  Othersalso make the value immutable, thus whena constant usesa list, thelistcannot be changed.  InVim9 we can use both.E1021E1307:constis used for making both the variable and the valuea constant.  Usethis for composite structures that you want to make sure will not be modified.Example:const myList = [1, 2]myList = [3, 4]# Error!myList[0] = 9# Error!myList->add(3)# Error!:finalE1125:finalis used for making only the variablea constant, the value can bechanged.  Thisis well known from Java.  Example:final myList = [1, 2]myList = [3, 4]# Error!myList[0] = 9# OKmyList->add(3)# OKItis common to write constantsas ALL_CAPS, but you don't have to.The constant only applies to the value itself, not whatit refers to.final females = ["Mary"]const NAMES = [["John", "Peter"], females]NAMES[0] = ["Jack"]     # Error!NAMES[0][0] = "Jack"    # Error!NAMES[1] = ["Emma"]     # Error!NAMES[1][0] = "Emma"    # OK, now females[0] == "Emma"Omitting :call and :evalE1190Functions can be called without:call:writefile(lines, 'file')Using:callis still possible, but thisis discouraged.Amethod call withoutevalis possible, so longas the startis anidentifier or can't be anEx command.  Fora function either "(" or "->"mustbe following, withouta line break.  Examples:myList->add(123)g:myList->add(123)[1, 2, 3]->Process(){a: 1, b: 2}->Process()"foobar"->Process()("foobar")->Process()'foobar'->Process()('foobar')->Process()In the rarecase thereis ambiguity betweena function name and anEx command,prepend ":" to make clear you want to use theEx command.  For example, thereis both the:substitute command and thesubstitute() function.  When theline starts withsubstitute( this will use the function. Prependa colon touse the command instead::substitute(pattern (replacement (If theexpression starts with "!" thisis interpretedasa shell command, notnegation ofa condition.  Thus thisisa shell command:!shellCommand->somethingPut theexpression in parentheses to use the "!" for negation:(!expression)->Method()Note that whilevariables need to be defined before they can be used,functions can be called before being defined.  Thisis required to allowfor cyclic dependencies between functions.  Itis slightlyless efficient,since the function has to be looked up by name.  Anda typo in the functionname will only be found when the functionis called.Omitting function()A user defined function can be usedasa functionreference in anexpressionwithoutfunction(). The argument types and return type will then be checked.The functionmust already have been defined.var Funcref = MyFunctionWhen usingfunction() the resulting typeis "func",a function with anynumber of arguments and any return type (including void).  The function can bedefined later if the argumentis in quotes.Lambda using => instead of ->vim9-lambdaIn legacyscript there can be confusion between using "->" foramethod calland fora lambda.  Also, whena "{"is found the parser needs to figure out ifitis the start ofalambda ora dictionary, whichis now more complicatedbecause of the use of argument types.To avoid these problemsVim9script usesa differentsyntax fora lambda,whichis similar to #"motion.txt.html#is">is allowed in the arguments ofalambda up to and including the"=>" (so that Vim can tell the difference between anexpression in parenthesesandlambda arguments).  Thisis OK:filter(list, (k, v) =>v > 0)This does not work:filter(list, (k, v)=> v > 0)This also does not work:filter(list, (k,v) => v > 0)But you can useabackslash to concatenate the lines before parsing:filter(list, (k,\v)\=> v > 0)vim9-lambda-argumentsE1172In legacyscriptalambda could be called with any number of extra arguments,there was no way to warn for not using them.  InVim9script the number ofargumentsmust match.  If youdo want to accept any arguments, or any furtherarguments, use "..._", which makes the function acceptvim9-variable-arguments.  Example:var Callback = (..._) => 'anything'echo Callback(1, 2, 3)  # displays "anything"inline-functionE1171Additionally,alambda can contain statements in {}:var Lambda = (arg) => {g:was_called = 'yes'return expression    }This can be useful fora timer, for example:var count = 0var timer = timer_start(500, (_) => { count += 1 echom 'Handler called ' .. count     }, {repeat: 3})The ending "}"must beat the start ofa line.  It can be followed by othercharacters, e.g.:var d = mapnew(dict, (k, v): string => {     return 'value'   })No command can follow the "{", onlya comment can be used there.command-blockE1026The block can also be used for defininga user command.  Inside the blockVim9syntax will be used.Thisis an example of using here-docs:    com SomeCommand {        g:someVar =<< trim eval END          ccc          ddd        END      }If the statements includea dictionary, its closing bracketmust not bewrittenat the start ofa line.  Otherwise,it would be parsedas theend ofthe block.  This does not work:command NewCommand {     g:mydict = {       'key': 'value',       }  # ERROR: will be recognized as the end of the block   }Put the '}' after the last item to avoid this:command NewCommand {     g:mydict = {       'key': 'value' }   }Rationale: The "}" cannot be aftera command becauseit would require parsingthe commands to find it.  For consistency with that no command can follow the"{".  Unfortunately this means using "() => {  command  }" does not work, linebreaks are always required.vim9-curlyTo avoid the "{" ofa dictionary literal to be recognizedasa statement blockwrapit in parentheses:var Lambda = (arg) => ({key: 42})Also when confused with the start ofa command block:({    key: value })->method()Automatic line continuationvim9-line-continuationE1097In many casesitis obvious that anexpression continues on the next line.  Inthose cases thereis no need to prefix the line withabackslash (seeline-continuation).  For example, whenalist spans multiple lines:var mylist = ['one','two',]And whenadict spans multiple lines:var mydict = {one: 1,two: 2,}Witha function call:var result = Func(arg1,arg2)For binary operators in expressions not in [],{} or ()a line breakispossible just before or after the operator.  For example:var text = lead   .. middle   .. endvar total = start +    end -    correctionvar result = positive? PosFunc(arg): NegFunc(arg)Foramethod call using "->" anda member usinga dot,a line breakis allowedbefore it:var result = GetBuilder()->BuilderSetWidth(333)->BuilderSetHeight(777)->BuilderBuild()var result = MyDict.memberFor commands that have an argument thatisalist of commands, the | characterat the start of the line indicates line continuation:autocmd BufNewFile *.match if condition|   echo 'match'| endifNote that this means that in heredoc the first line cannot start witha bar:var lines =<< trim END   | this doesn't workENDEither use an empty lineat the start ordo not use heredoc.  Or temporarilyadd the "C" flag to'cpoptions':set cpo+=Cvar lines =<< trim END   | this worksENDset cpo-=CIf the heredocis insidea function'cpoptions'must be set before:def andrestored after the :enddef.In places where line continuation withabackslashis still needed, suchassplitting upa longEx command, comments can start with '#\ ':syn region Text      \ start='foo'      #\ comment      \ end='bar'Like with legacyscript'"\'is used.  Thisis also needed when linecontinuationis used withoutabackslash anda line starts witha bar:au CursorHold * echom 'BEFORE bar'      #\ some comment      | echom 'AFTER bar'E1050To makeit possible for theoperatorat the start of the line to berecognized,itis required toputa colon beforea range.  This example willadd "start" and "print":var result = start+ printLike this:var result = start + printThis will assign "start" and printa line:var result = start:+ printAfter the range anEx commandmust follow.  Without the colon you can callafunction without:call, but aftera range youdo need it:MyFunc():% call MyFunc()Note that the colonis not required for the+cmd argument:edit +6 fnameItis also possible to splita function header over multiple lines, in betweenarguments:def MyFunc(text: string,separator = '-'): stringSincea continuation line cannot be easily recognized the parsing of commandshas been made stricter.  E.g., because of the error in the first line, thesecond lineis seenasa separate command:popup_create(some invalid expression, {   exit_cb: Func})Now "exit_cb: Func})"is actuallya valid command: save any changes to thefile "_cb: Func})" and exit.  To avoid this kind of mistake inVim9scripttheremust be whitespace between most command names and the argument.E1144However, the argument ofa command thatisa command won't be recognized.  Forexample, after "windo echoexpr"a line break inside "expr" will not be seen.Notes:- "enddef" cannot be usedat the start ofa continuation line,it ends the  current function.- No line breakis allowed in the LHS of an assignment.  Specifically when  unpackingalist:let-unpack. Thisis OK:[var1, var2] =Func()  This does not work:[var1,    var2] =Func()- No line breakis allowed in between arguments of an:echo,:execute and  similar commands.  Thisis OK:echo [1,2] [3,4]  This does not work:echo [1, 2][3, 4]- In some casesitis difficult for Vim to parsea command, especially when  commands are usedas an argument to another command, suchas:windo.  In  those cases the line continuation withabackslash has to be used.White spacevim9-white-spaceE1004E1068E1069E1074E1127E1202Vim9script enforces proper use of white space.  Thisis no longer allowed:var name=234# Error!var name= 234# Error!var name =234# Error!Theremust be whitespace before and after the "=":var name = 234# OKWhitespacemust also beput before the# that startsa comment afteracommand:var name = 234# Error!var name = 234 # OKWhitespaceis required around most operators.Whitespaceis required inasublist (list slice) around the ":", exceptatthe start and end:otherlist = mylist[v : count]# v:count has a different meaningotherlist = mylist[:]# make a copy of the Listotherlist = mylist[v :]otherlist = mylist[: v]Whitespaceis not allowed:- Betweena function name and the "(":Func (arg)   # Error!Func     \ (arg)   # Error!Func      (arg)   # Error!Func(arg)   # OKFunc(      arg)   # OKFunc(      arg   # OK      )E1205Whitespaceis not allowed ina:set command between the option name andafollowing "&", "!", "<", "=", "+=", "-=" or "^=".No curly braces expansioncurly-braces-names cannot be used.Command modifiers are not ignoredE1176Usinga command modifier fora command that does not useit gives an error.E1082Also, usinga command modifier withouta following commandis now an error.Dictionary literalsvim9-literal-dictE1014Traditionally Vim has supported dictionary literals witha{} syntax:let dict = {'key': value}Laterit became clear that usinga simple text keyis very common, thusliteral dictionaries were introduced ina backwards compatible way:let dict = #{key: value}However, this#{}syntaxis unlike any existing language.  Asit turns outthat usinga literal keyis much more common than using an expression, andconsidering that JavaScript uses this syntax, using the{} form for dictionaryliteralsis considereda much more useful syntax.  InVim9script the{} formuses literal keys:var dict = {key: value}This works for alphanumeric characters, underscore and dash.  If you want touse another character, usea single or double quoted string:var dict = {'key with space': value}var dict = {"key\twith\ttabs": value}var dict = {'': value}# empty keyE1139Incase the key needs to be an expression, square brackets can be used, justlike in #"key" .. nr]: value}The key type can be string, number, bool or float.  Other types result in anerror.  Without using[] the valueis usedasa string, keeping leading zeros.Anexpression given with[]is evaluated and then converted toa string.Leading zeros will then be dropped:var dict = {000123: 'without', [000456]: 'with'}echo dict{'456': 'with', '000123': 'without'}A float only works inside[] because the dotis not accepted otherwise:var dict = {[00.013]: 'float'}echo dict{'0.013': 'float'}No :xit, :t, :k, :append, :change or :insertE1100These commands are too easily confused with local variable names.Instead of:x or:xit you can use:exit.Instead of:t you can use:copy.Instead of:k you can use:mark.ComparatorsThe'ignorecase' optionis not used for comparators that use strings.Thus "=~" works like "=~#"."is" and "isnot"(expr-is andexpr-isnot) when used on strings now returnfalse.  In legacyscript they just compare the strings, inVim9script theycheck identity, and strings are copied when used, thus two strings are neverthe same (this might change someday if strings are not copied butreferencecounted).Abort after errorIn legacy script, when an erroris encountered, Vim continues to executefollowing lines.  This can lead toa long sequence oferrors and need to typeCTRL-C to stop it.  InVim9script execution of commands stopsat the firsterror.  Example:vim9scriptvar x = does-not-existecho 'not executed'For loopE1254The loop variablemust not be declared yet:var i = 1for i in [1, 2, 3]   # Error!Itis possible to usea global variable though:g:i = 1for g:i in [1, 2, 3]  echo g:iendforLegacy Vimscript has some tricks to makea for loop overalist handledeleting itemsat the current or previous item.  InVim9scriptit just usesthe index, if items are deleted then items in thelist will be skipped.Example legacy script:let l = [1, 2, 3, 4]for i in l   echo i   call remove(l, index(l, i))endforWould echo:1234In compiledVim9script you get:13Generally, you should not change thelist thatis iterated over.  Makea copyfirst if needed.When looping overalist of lists, the nested lists can be changed.  The loopvariableis "final",it cannot be changed but what its value can be changed.E1306The depth of loops,:for and:while loops added together, cannot exceed 10.Conditions and expressionsvim9-booleanConditions and expressions are mostly working like theydo in other languages.Some values are different from legacy Vim script:valuelegacy Vim scriptVim9 script0falsyfalsy1truthytruthy99truthyError!"0"falsyError!"99"truthyError!"text"falsyError!For the "??"operator and when using "!" then thereis no error, every valueis eitherfalsy or truthy.  Thisis mostly like JavaScript, except that anemptylist anddictis falsy:typetruthy whenbooltrue,v:true or 1numbernon-zerofloatnon-zerostringnon-emptyblobnon-emptylistnon-empty (different from JavaScript)tuplenon-empty (different from JavaScript)dictionarynon-empty (different from JavaScript)funcwhen thereisa function namespecialtrue orv:truejobwhen not NULLchannelwhen not NULLclassnot applicableobjectwhen not NULLenumnot applicableenum valuealwaystypealiasnot applicableTheboolean operators "||" and "&&" expect the values to be boolean, zero orone:1 || false   == true0 || 1       == true0 || false   == false1 && true    == true0 && 1       == false8 || 0     Error!'yes' && 0   Error![] || 99     Error!When using "!" for inverting, thereis no error for using any type and theresultisa boolean.  "!!" can be used to turn any value into boolean:!'yes'== false!![]== false!![1, 2, 3]== trueWhen using ".." forstring concatenation arguments of simple types arealways converted to string:'hello ' .. 123  == 'hello 123''hello ' .. v:true  == 'hello true'Simple types are Number, Float,Special and Bool.  For other typesstring()should be used.falsetruenullnull_blobnull_channelnull_classnull_dictnull_functionnull_jobnull_listnull_objectnull_partialnull_stringE1034InVim9script one can use the following predefined values:truefalsenullnull_blobnull_channelnull_classnull_dictnull_functionnull_jobnull_listnull_tuplenull_objectnull_partialnull_stringtrueis the sameasv:true,false the sameasv:false,null the sameasv:null.Whilenull has the type "special", the other "null_" values have the typeindicated by their name.  Quite oftenanull valueis handled the sameas anempty value, but not always.  The values can be useful to clearascript-localvariable, since they cannot be deleted with:unlet.  E.g.:var theJob = job_start(...)# let the job do its worktheJob = null_jobThe values can also be usefulas the default value for an argument:def MyFunc(b: blob = null_blob)    # Note: compare against null, not null_blob,    #       to distinguish the default value from an empty blob.    if b == null        # b argument was not givenSeenull-compare for more information abouttesting against null.Itis possible to comparenull  with any value, this will not givea typeerror.  However, comparingnull witha number, float or bool will alwaysresult infalse.  Thisis different from legacy script, where comparingnull with zero orfalse would returntrue.vim9-false-trueWhen convertingaboolean toastringfalse andtrue are used, notv:false andv:true like in legacy script.v:none has nononereplacement,it has no equivalent in other languages.vim9-string-indexIndexingastring with[idx] or takingaslice with [idx: idx] uses characterindexes instead of byte indexes.  Composing characters are included.Example:echo 'bár'[1]In legacyscript this results in the character 0xc3 (an illegal byte), inVim9script this results in thestring 'á'.A negativeindexis counting from the end, "[-1]"is the last character.To exclude the last character useslice().Tocount composing characters separately usestrcharpart().If theindexis out of range then an emptystring results.In legacyscript "++var" and "--var" would be silently accepted and have noeffect.  Thisis an error inVim9 script.Numbersstarting with zero are not considered to be octal, only numbersstarting with "0o" are octal: "0o744".scriptversion-4What to watch out forvim9-gotchasVim9 was designed to be closer to often used programming languages, butat thesame time tries to support the legacy Vim commands.  Some compromises had tobe made.  Hereisa summary of what might be unexpected.Ex command ranges need to be prefixed witha colon.->  legacy Vim: shifts the previous line to the right->func()  Vim9: method call in a continuation line:->  Vim9: shifts the previous line to the right%s/a/b  legacy Vim: substitute on all linesx = alongname     % another  Vim9: modulo operator in a continuation line:%s/a/b  Vim9: substitute on all lines't  legacy Vim: jump to mark t'text'->func()  Vim9: method call:'t  Vim9: jump to mark tSomeEx commands can be confused with assignments inVim9 script:g:name = value    # assignment:g:pattern:cmd  # :global commandTo avoid confusion betweena:global or:substitute command and anexpression or assignment,a few separators cannot be used when these commandsare abbreviated toa single character: ':', '-' and '.'.g:pattern:cmd  # invalid command - ERRORs:pattern:repl  # invalid command - ERRORg-pattern-cmd  # invalid command - ERRORs-pattern-repl  # invalid command - ERRORg.pattern.cmd  # invalid command - ERRORs.pattern.repl  # invalid command - ERRORAlso, there cannot beaspace between the command and the separator:g /pattern/cmd  # invalid command - ERRORs /pattern/repl  # invalid command - ERRORFunctions defined with:def compile the whole function.  Legacyfunctionscan bail out, and the following lines are not parsed:func Maybe()  if !has('feature')    return  endif  use-featureendfuncVim9functions are compiledasa whole:def Maybe()  if !has('feature')    return  endif  use-feature  # May give a compilation errorenddefFora workaround, splitit in two functions:func Maybe()  if has('feature')    call MaybeInner()  endifendfuncif has('feature')  def MaybeInner()    use-feature  enddefendifOrput the unsupported code inside anif witha constantexpression thatevaluates to false:def Maybe()  if has('feature')    use-feature  endifenddefTheexists_compiled() function can also be used for this.vim9-user-commandAnother side effect of compilinga functionis that the presence ofa usercommandis checkedat compile time.  If the user commandis defined later anerror will result.  This works:command -nargs=1 MyCommand echom <q-args>def Works()  MyCommand 123enddefThis will give an error for "MyCommand" not being defined:def Works()  command -nargs=1 MyCommand echom <q-args>  MyCommand 123enddefA workaroundis to invoke the command indirectly with:execute:def Works()  command -nargs=1 MyCommand echom <q-args>  execute 'MyCommand 123'enddefNote that for unrecognized commands thereis no check for "|" anda followingcommand.  This will give an error for missingendif:def Maybe()  if has('feature') | use-feature | endifenddefOther differencesPatterns are used like'magic'is set, unless explicitly overruled.The'edcompatible' option valueis not used.The'gdefault' option valueis not used.You may also find this wiki useful.  It was written by an early adopter ofVim9 script:https://github.com/lacygoill/wiki/blob/master/vim/vim9.md:++:--The ++ and-- commands have been added.  They are very similar to adding orsubtracting one:++varvar += 1--varvar -= 1Using ++var or --var in anexpressionis not supported yet.==============================================================================3. New stylefunctionsfast-functions:def:def[!]{name}([arguments])[:{return-type}]Definea new function by the name{name}.  The body ofthe function follows in the next lines, until thematching:enddef.E1073The{name} cannot be reusedat thescript-local level:  vim9script  def F_1073()  enddef  def F_1073() # E1073: Name already defined: <SNR>...  enddefE1011The{name}must beless than 100 bytes long.E1077{arguments}isa sequence of zero or more argumentdeclarations.  There are three forms:{name}:{type}{name}={value}{name}:{type}={value}The first formisa mandatory argument.  So, thedeclarationmust providea type.  Example:  vim9script  def F_1077(x): void      # E1077: Missing argument type for x  enddefFor the second form, because the declaration does notspecify it, Vim infers the type.  For both second andthird forms,a default{value} applies when thecaller omits it.  Examples:  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()E1123Arguments ina builtin function called ina:deffunctionmust have commas between arguments:  vim9script  def F_1123(a: number, b: number): void      echo max(a b)      # E1123: Missing comma before argument: b)  enddef  F_1123(1, 2)E1003E1027E1096The type of value used with:returnmust match{return-type}.  When{return-type}is omitted oris"void" the functionis not allowed to returnanything.  Examples:  vim9script  def F_1003(): bool      return  # E1003: Missing return value  enddef  F_1003()  vim9script  def F_1027(): bool      echo false  # E1027: Missing return statement  enddef  F_1027()  vim9script  def F_1096(): void      return false  # E1096: Returning a value ...  enddef  F_1096()E1056E1059When ":{return-type}"is specified,{return-type}cannot be omitted (leavinga hanging colon).  The ": "also cannot be preceded by white space.  Examples:  def F_1056():                              # E1056: Expected a type:  enddef  def F_1059() : bool                              # E1059: No white space allowed before colon:...  enddefThe function will be compiled into instructions whencalled or when either:defcompile or:disassembleisused.  (For an example, see:disassemble.)Syntaxand typeerrors will be producedat that time.E1058Itis possible to nest:def inside another:def or:function only up to 49 levels deep.  At 50 or morelevels,itisaE1058 error.E1117[!]is allowed only in legacy Vimscript becauseitpermits function redefinition (as with:function!).InVim9 script,!is not allowed becausescript-localfunctions cannot be deleted or redefined, though theycan be removed by reloading the script.  Also, nestedfunctions cannot use! for redefinition.  Examples:  " Legacy Vim script :def! example  def! LegacyFunc()      echo "def! is allowed in a legacy Vim script"  enddef  call LegacyFunc()  vim9script  def Func()      def! InnerFunc()          # E1117: Cannot use ! with nested :def      enddef  enddef  Func()  vim9script  def! F_477(): void  # E477: No ! allowed  enddef  vim9script  def F_1084(): void  enddef  delfunction! F_1084  # E1084: Cannot delete Vim9 script function F_1084Note: The generic errorE1028("Compiling:deffunction failed") indicates an undeterminable errorduring compilation.  If reproducible,it may bereportedathttps://github.com/vim/vim/issuesasit could representa gap in Vim's error reporting.:enddefE1057E1152E1173:enddefEnd ofa function defined with:def.  It should be ona line by itself.  Examples:  vim9script  def MyFunc()  echo 'Do Something' | enddef  # E1057: Missing :enddef  vim9script  def F_1173()  enddef echo 'X'  # E1173: Text found after enddef: echo 'X'  vim9script  def F_1152()      function X()      enddef  # E1152: Mismatched enddef  enddefYou may also find this wiki useful.  It was written by an early adopter ofVim9 script:https://github.com/lacygoill/wiki/blob/master/vim/vim9.mdIf thescript the:def functionis defined inisVim9 script,script-localvariablesmust be accessed without using the "s:" prefix.  Theymust bedefined before the functionis compiled and thereis no way to avoiderrors(e.g., by usingexists()) to conditionally skip undeclared variables.For example:vim9scriptdef MyVim9def()    echo unus       # Echoes 1    # echo s:unus   # This would be E1268 (Cannot use s: in Vim9)    if exists('duo')        # echo duo  # This would be E1001 (Variable not found: duo)    endifenddefvar unus: number = 1MyVim9def()         # MyVim9def is compiled ("duo" does not exist yet)var duo: number = 2If thescript the:def functionis defined inis legacy Vim script,script-localvariables may be accessed with or without the "s:" prefix.However, using "s:" may defer variable resolution to runtime, avoidingcompilationerrors forvariables that may not exist yet,as this exampleexplains:" legacy Vim scriptdef! MyLegacyDef(): void    echo [unus, s:unus]   # Echoes [1, 1]    # (If uncommented) First sourcing of 'echo s:duo' is E121 and    # causes a compilation error; subsequent sourcing echoes 2:    # echo s:duo    if exists("s:duo")        # First sourcing: skips echo; subsequent sourcing: echoes 2        echo s:duo    endif    if exists("duo")        # (If uncommented) First sourcing of 'echo duo' is E1001 and        # causes a compilation error; subsequent sourcing echoes 2:        # echo duo    endifenddeflet s:unus = 1call MyLegacyDef()  " Calls MyLegacyDef() and compiles if not alreadylet s:duo = 2E1269Script-localvariables inaVim9scriptmust be declaredat thescriptlevel.  They cannot be created ina:def function and may not be declaredina legacy function with the "s:" prefix.  For example:vim9scriptfunction F_1269()    let s:i_wish = v:trueendfunctionF_1269()# E1269: Cannot create a Vim9 script variable in a function: s:i_wish:defc:defcompile:defc[ompile]Compilefunctions and classes(class-compile)defined in the currentscript that were not compiledyet.  This will report anyerrors found duringcompilation.Example: When the three lines (up to and includingenddef) are sourced, thereis no error because theVim9:def functionis not compiled.  However, if allfour lines are sourced, compilation fails:  vim9script  def F_1027(): string  enddef  defcompile F_1027  # E1027: Missing return statement:defc[ompile] MyClassCompile all methods ina class.  (See:disassemblefor an example.):defc[ompile]{func}:defc[ompile] debug{func}:defc[ompile]profile{func}Compile function{func}, if needed.  Use "debug" and"profile" to specify the compilation mode.This will report anyerrors found during compilation.{func} can also be "ClassName.functionName" tocompilea function ormethod ina class.{func} can also be "ClassName" to compile allfunctions and methods ina class.:disa:disassemble:disa[ssemble]{func}Show the instructions generated for{func}.Thisis for debugging and testing.If{func}is not found, errorE1061 occurs.{func} can also be "ClassName.functionName" todisassemblea function ina class.The following example demonstrates using:defcompilewithaclass and:disassemble witha"ClassName.functionName" (positioning the cursor onthe last line of the visually sourced script):  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()  # Cursor is positioned here->_:disa[ssemble]profile{func}Like:disassemble but with the instructions used forprofiling.:disa[ssemble] debug{func}Like:disassemble but with the instructions used fordebugging.Note: For command line completion of{func},script-localfunctionsare shown with their<SNR>.  Depending on options, includingwildmenumode(), completion may work with "s:", "<S", or the functionname directly.  (For example, in Vim started with-u NONE, ":disa s:"andc_CTRL-E listsscript-local function names.)LimitationsVariables local to:deffunctions are not visible tostring evaluation.The following example shows that thescript-local constant "SCRIPT_LOCAL"isvisible whereas the function-local constant "DEF_LOCAL"is not:vim9scriptconst 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]')    endifenddefecho 'script local'->MapList()# Echoes ['script-local']echo 'def local'->MapList()# E121: Undefined variable: DEF_LOCALThe map argumentisastring expression, whichis evaluated without thefunction scope.  Instead, inVim9 script, usea lambda:vim9scriptdef MapList(): list<string>    const DEF_LOCAL: list<string> = ['A', 'def-local', 'list']    return [1]->map((_, v) => DEF_LOCAL[v])enddefecho MapList()# Echoes ['def-local']For commands that are not compiled, suchas:edit,backtick-expansion canbe used andit can use the local scope.  Example:vim9scriptdef EditNewBlah()    var fname: string = 'blah.txt'    split    edit `=fname`enddefEditNewBlah()  # A new split is created as buffer 'blah.txt'Closures defined ina loop can either sharea variable or each have their owncopy, depending on where the variableis declared.  Witha variable declaredoutside the loop, all closuresreference the same shared variable.The following example demonstrates the consequences, with the "outloop"variable existing only once:vim9scriptvar flist: list<func>def ClosureEg(n: number): void    var outloop: number = 0  # outloop is declared outside the loop!    for i in range(n)        outloop = i        flist[i] = (): number => outloop  # Closures ref the same var    endfor    echo range(n)->map((i, _) => flist[i]())enddefClosureEg(4)  # Echoes [3, 3, 3, 3]All closuresput in thelist refer to the same instance, which, in the end,is 3.However, when the variableis declared inside the loop, eachclosure gets itsown copy,as shown in this example:vim9scriptvar flist: list<func>def ClosureEg(n: number): void    for i in range(n)        var inloop: number = i  # inloop is declared inside the loop        flist[i] = (): number => inloop  # Closures ref each inloop    endfor    echo range(n)->map((i, _) => flist[i]())enddefClosureEg(4)  # Echoes [0, 1, 2, 3]Another way to havea separate context for eachclosureis to callafunction to define it:vim9scriptdef GetClosure(i: number): func    var infunc: number = i    return (): number => infuncenddefvar 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]())enddefClosureEg(4)  # Echoes [0, 1, 2, 3]E1271Aclosuremust be compiled in the context thatitis defined in, so thatvariables in that context can be found.  This mostly happens correctly,except whena functionis marked for debugging with:breakadd afterit wascompiled.  Make sure to define the breakpoint before compiling the outerfunction.E1248In some situations, suchas whenaVim9closure which captures localvariablesis converted toastring and then executed, an error occurs.  This happensbecause thestring execution context cannot access the localvariables fromthe original context where theclosure was defined.  For example:vim9scriptdef F_1248(): void    var n: number    var F: func = () => {        n += 1    }    try        execute printf("call %s()", F)    catch        echo v:exception    endtryenddefF_1248()  # Vim(call):E1248: Closure called from invalid contextInVim9 script,a loop variableis invalid after the loopis closed.For example, thistimer will echo0 to 2 on separate lines.  However, ifthe variable "n"is used after the:endfor, thatis anE121 error:vim9scriptfor n in range(3)    var nr: number = n    timer_start(1000 * n, (_) => {        echowindow nr    })endfortry    echowindow ncatch    echo v:exceptionendtryNote: Using:echowindowis useful inatimer becausemessagesgointoapopup and will not interfere with what the userisdoing whenit triggers.Converting a :function to a :defconvert_legacy_function_to_vim9convert_:function_to_:defThere are many changes that need to be made to converta:function toa:def function.  The following are some of them:- Changelet used to declarevariables to one ofvar,const, orfinal,  and remove the "s:" from eachscript-variable.- Changefunc orfunction todef.- Changeendfunc orendfunction toenddef.- Add the applicable type (or "any") to each function argument.- Remove "a:" from eachfunction-argument.- Remove inapplicableoptions suchas:func-range,:func-abort,:func-dict, and:func-closure.- If the function returns something, add the return type.  (Ideally, add  "void" ifit does not return anything.)- Remove line continuation backslashes from places they are not required.- Removelet for assigning values tog:,b:,w:,t:, orl: variables.- Rewritelambda expressions inVim9scriptsyntax (seevim9-lambda).- Change comments to start with# (preceded by white space) instead of ".-Insert whitespace in expressions where required (seevim9-white-space).- Change "." used forstring concatenation to " .. ".  (Alternatively, use  aninterpolated-string.)The following legacy Vimscript andVim9script examples demonstrate allthose differences.  First, legacy Vim script:let s:lnum=0function 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)"no space before commentendfunctioncall Leg8(10)->popup_notification(#{time: 3000})" Pops up 'Result: 42'The equivalent inVim9 script:vim9scriptvar lnum: numberdef 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) # space required before # commentenddefVim9(10)->popup_notification({time: 3000}) # Pops up 'Result: 42'Note: This example also demonstrates (outside the:def function):- Removing "#" from the legacy#{}- seevim9-literal-dict- Omitting:call (allowed, though unnecessary inVim9 script)Calling a :def function in an expr optionexpr-option-functionThe value ofa few options, suchas'foldexpr',is anexpression thatisevaluated to geta value.  The evaluation can have quitea bit of overhead.One way to minimize the overhead, and also to keep the option value simple,is to definea compiled function and set the option to callit withoutarguments.  For example:vim9scriptdef MyFoldFunc(): string    # This matches start of line (^), followed by a digit, a full stop    # a space or tab, an uppercase character, with an empty next line    return getline(v:lnum) =~ '^[[:digit:]]\.[[:blank:]][[:upper:]]'        && getline(v:lnum + 1)->empty() ? '>1' : '1'enddefset foldexpr=MyFoldFunc()set foldmethod=exprnorm! zM  Warning: Thisscript creates and appliesfoldsat the "Heading 1" level of   thisvim9.txthelp buffer.  (You can usezR, inNormal mode, to   open all thefolds after sourcing the script.)==============================================================================4. Typesvim9-typesThe following types, each shown with its corresponding internalv:t_TYPEvariable, are supported:numberv:t_numberstringv:t_stringfuncv:t_funcfunc:{type}v:t_funcfunc({type}, ...)v:t_funcfunc({type}, ...):{type}v:t_funclist<{type}>v:t_listdict<{type}>v:t_dictfloatv:t_floatboolv:t_boolnonev:t_nonejobv:t_jobchannelv:t_channelblobv:t_blobclassv:t_classobjectv:t_objecttypealiasv:t_typealiasenumv:t_enumenumvaluev:t_enumvaluetuple<{type}>v:t_tupletuple<{type},{type}, ...>v:t_tupletuple<...list<{type}>>v:t_tupletuple<{type}, ...list<{type}>>v:t_tuplevoidE1031E1186These types can be used in declarations, though no simple value can have the"void" type.  Trying to usea voidasa value results in an error.  Examples:vim9scriptdef NoReturnValue(): voidenddeftry    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 ...    endtryendtryE1008E1009E1010E1012Ill-formed declarations and mismatching types result in errors.  The followingare examples oferrors E1008, E1009, E1010, and E1012:vim9cmd var l: listvim9cmd var l: list<numbervim9cmd var l: list<invalidtype>vim9cmd var l: list<number> = ['42']Thereis no array type.  Instead, use eitheralist ora tuple.  Those typesmay also be literals (constants).  In the following example, [5, 6]isalistliteral and (7,)atuple literal.  The echoedlistisalist literal too:vim9scriptvar l: list<number> = [1, 2]var t: tuple<...list<number>> = (3, 4)echo [l, t, [5, 6], (7, )]tuple-typeAtuple type may be declared in the following ways:tuple<number>atuple witha single item of typeNumbertuple<number, string>atuple with two items of typeNumber andStringtuple<number, float, bool>atuple with three items of typeNumber,Float andBooleantuple<...list<number>>a variadictuple with zero or more items oftypeNumbertuple<number, ...list<string>>atuple with an item of typeNumber followedby zero or more items of typeStringExamples:    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-tupleE1539A variadictuple has zero or more items of the same type.  The type ofavariadictuplemustend withalist type.  Examples:    var myTuple: tuple<...list<number>> = (1, 2, 3)    var myTuple: tuple<...list<string>> = ('a', 'b', 'c')    var myTuple: tuple<...list<bool>> = ()vim9-func-declarationE1005E1007vim9-partial-declarationvim9-func-typeA function (or partial) may be declared in the following ways:funcany kind of function reference, no typechecking for arguments or return valuefunc: voidany number and type of arguments, no returnvaluefunc:{type}any number and type of arguments with specificreturn typefunc()function with no argument, does not returnavaluefunc(): voidsamefunc():{type}function with no argument and return typefunc({type})function with argument type, does not returna valuefunc({type}):{type}function with argument type and return typefunc(?{type})function with type of optional argument, doesnot returna valuefunc(...list<{type}>)function with type oflist for variable numberof arguments, does not returna valuefunc({type}, ?{type}, ...list<{type}>):{type}function with:- type of mandatory argument- type of optional argument- type oflist for variable number of  arguments- return typeIf the return typeis "void" the function does not returna value.Thereference can also beaPartial, in whichcaseit stores extra argumentsand/ora dictionary, which are not visible to the caller.  Since they arecalled in the same way, the declarationis the same.  This interactive exampleprompts fora circle's radius and returns its area to two decimal places,usinga partial:vim9scriptdef CircleArea(pi: float, radius: float): float    return pi * radius->pow(2)enddefconst 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-typeCustom types(typealias) can be defined with:type.  Theymust start witha capitalletter (which avoids name clashes with either current or futurebuiltin types) similar to user functions.  This example createsalist ofperfect squares and reports ontype() (14,a typealias) and thetypename():vim9scripttype Ln = list<number>final perfect_squares: Ln = [1, 4, 9, 16, 25]echo "Typename (Ln): " ..     $"type() is {Ln->type()} and typename() is {Ln->typename()}"E1105Atypealias itself cannot be converted toa string:vim9scripttype Ln = list<number>const FAILS: func = (): string => {    echo $"{Ln}"  # E1105: Cannot convert typealias to string    }vim9-class-typevim9-interface-typevim9-object-typeAclass,object, andinterface may all be usedas types.  The followinginteractive example prompts fora float value and returns the area of twodifferent shapes.  It also reports on thetype() andtypename() of theclasses, objects, and interface:vim9scriptinterface Shape    def InfoArea(): tuple<string, float>endinterfaceclass Circle implements Shape    var radius: float    def InfoArea(): tuple<string, float>        return ('Circle (π × r²)', 3.141593 * this.radius->pow(2))    enddefendclassclass Square implements Shape    var side: float    def InfoArea(): tuple<string, float>        return ('Square (s²)', this.side->pow(2))    enddefendclassconst 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}"endforecho "\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-typevim9-enumvalue-typeAnenum may be usedasa type(v:t_enum).  Variables holdingenum valueshave theenumvalue type(v:t_enumvalue)at runtime.  The followinginteractive example prompts fora character and returns information abouteithera square ora rhombus.  It also reports on thetype() andtypename()of theenum and enumvalue:vim9scriptenum 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"    enddefendenumecho "Rhombus (r) or Square (s)?"var myQuad: Quad = getcharstr() =~ '\c^R' ? Quad.Rhombus : Quad.Squareecho myQuad.string() .. "\ttype()\ttypename()"echo $"Quad  \t{Quad->type()}  \t{Quad->typename()}"echo $"myQuad\t{myQuad->type()}\t{myQuad->typename()}"Notes: Thisscript uses builtinmethod "string()"(object-string()).Thetypename() of Quad and myQuad are the same("enum<Quad>")whereas thetype()is distinguished (myQuad returns 16,enumvalue, whereas Quad returns 15, enum).Variable types and type castingvariable-typesVariables declared inVim9script or ina:def function havea type, eitherspecified explicitly or inferred from the initialization.Global, buffer,window andtab pagevariablesdo not havea specific type.Consequently, their values may changeat any time, possiblychanging the type.Therefore, in compiled code, the "any" typeis assumed.This can bea problem when stricter typingis desired, for example, whendeclaringa list:var l: list<number> = [1, b:two]Since Vim doesn't know the type of "b:two", theexpression becomes list<any>.A runtime check verifies thelist matches the declared type before assignment.type-castingTo get more specific type checking, use type casting.  This checks thevariable's type before building the list, rather than checking whetherlist items match the declared type.  For example:var l: list<number> = [1, <number>b:two]So, here the type cast checks whether "b:two"isa number and gives an errorifit isn't.The differenceis demonstrated in the following example.  With funcrefvariable "NTC", Vim infers theexpression type "[1, b:two]"as list<any>, thenverifies whetherit can be assigned to the list<number> return type.  Withfuncref variable "TC", the type cast means Vim first checks whether "b:two"isa<number> type:vim9scriptb:two = '2'const NTC: func = (): list<number> => {    return [1, b:two]    }disassemble NTC  # 3 CHECKTYPE list<number> stack [-1]try    NTC()catch    echo v:exception .. "\n\n"  # expected list<number> but...endtryconst TC: func = (): list<number> => {    return [1, <number>b:two]    }disassemble TC  # 2 CHECKTYPE number stack [-1]try    TC()catch    echo v:exception  # expected number but got stringendtryNote: Notice how the errormessages differ, showing when        type checking occurs.E1104Thesyntax ofa type castis "<{type}>".  An error occurs if either theopening "<"(E121) or closing ">" (E1104)is omitted.  Also, whitespaceis not allowed either after the "<"(E15) or before the ">"(E1068), whichavoids ambiguity with smaller-than and greater-than operators.Althougha type casting forces explicit type checking,it neither changes thevalue of, nor the type of,a variable.  If you need to alter the type, useafunction suchasstring() to convert toa string, orstr2nr() to convertastring toa number.If type castingis applied toa chained expression,itmust be compatible withthe final result.  Examples:vim9script# These type casts workecho <list<any>>[3, 2, 1]->extend(['Go!'])echo <string>[3, 2, 1]->extend(['Go!'])->string()echo <tuple<...list<number>>>[3, 2, 1]->list2tuple()# This type cast failsecho <number>[3, 2, 1]->extend(['Go!'])->string()E1272Ifa typeis used ina context where types are not expected you can getE1272.  For example::vim9cmd echo islocked('x: string')Note: Thismust be executed from Vim's command line, not sourced.E1363Ifa typeis incomplete, suchas when an object'sclassis unknown,E1363results.  For example:vim9scriptvar E1363 = null_class.member  # E1363: Incomplete typeAnothernull object-related errorisE1360:vim9scriptvar obj = null_objectvar E1360 = obj.MyMethod()  # E1360: Using a null objectType inferencetype-inferenceDeclaring types explicitly provides many benefits, including targeted typechecking and clearer error messages.  Nonetheless, Vim often can infer typesautomatically when they are omitted.  For example, each of these variables'types are inferred, with thetype() andtypename() echoed showing thoseinferred types:vim9scriptecho "\t type()\t typename()"var b = true    | echo $"{b} \t {b->type()} \t {b->typename()}"var f = 4.2     | echo $"{f} \t {f->type()} \t {f->typename()}"var l = [1, 2]  | echo $"{l} \t {l->type()} \t {l->typename()}"var n = 42      | echo $"{n} \t {n->type()} \t {n->typename()}"var s = 'yes'   | echo $"{s} \t {s->type()} \t {s->typename()}"var t = (42, )  | echo $"{t} \t {t->type()} \t {t->typename()}"The type ofa list, tuple, or dictionaryis inferred from the common type ofits values.  When the values are all the same type, that typeis used.If thereisa mix of types, the "any" typeis used.  In the following example,the echoedtypename() for each literal demonstrates these points:vim9scriptecho [1, 2]->typename() # list<number>echo [1, 'x']->typename() # list<any>echo {ints: [1, 2], bools: [false]}->typename() # dict<list<any>>echo (true, false)->typename() # tuple<bool, bool>The common type of function references, when theydo not all have the samenumber of arguments,is indicated with "(...)", meaning the number ofargumentsis unequal.  Thisscript demonstratesa "list<func(...): void>":vim9scriptdef Foo(x: bool): voidenddefdef Bar(x: bool, y: bool): voidenddefvar funclist = [Foo, Bar]echo funclist->typename()Script-localvariables inaVim9script are type checked.  The typeisalso checked forvariables declared ina legacy function.  For example:vim9scriptvar my_local = (1, 2)function Legacy()    let b:legacy = [1, 2]endfunctionLegacy()echo $"{my_local} is type {my_local->type()} ({my_local->typename()})"echo $"{b:legacy} is type {b:legacy->type()} ({b:legacy->typename()})"E1013Whena typeis declared fora List, Tuple, or Dictionary, the typeis attachedto it.  Similarly, ifa typeis not declared, the type Vim infersis attached.In either case, if anexpression attempts to change the type,E1013 results.This example has its type inferred and demonstrates E1013:vim9scriptvar lb = [true, true]# Two bools, so Vim infers list<bool> typeecho lb->typename()# Echoes list<bool>lb->extend([0])# E1013 Argument 2: type mismatch, ...If you wanta permissive list, either explicitly use<any> or declare anemptylist initially (or both, i.e., `list<any>= []`).  Examples:vim9scriptfinal la: list<any> = []echo la->extend(['two', 1])final le = []echo le->extend(la)Similarly fora permissive dictionary:vim9scriptfinal da: dict<any> = {}echo da->extend({2: 2, 1: 'One'})final de = {}echo de->extend(da)->string()And, although tuples themselves are immutable, permissivetuple concatenationcan be achieved with either "any" or an empty tuple:vim9scriptvar t_any: tuple<...list<any>> = (3, '2')t_any = t_any + (true, )echo t_anyvar t_dec_empty = ()t_dec_empty = t_dec_empty + (3, '2', true)echo t_dec_emptyIfalist literal or dictionary literalis not bound toa variable, its typemay change,as this example shows:vim9scriptecho [3, 2, 1]->typename() # list<number>echo [3, 2, 1]->extend(['Zero'])->typename() # list<any>echo {1: ['One']}->typename() # dict<list<string>>echo {1: ['One']}->extend({2: [2]})->typename() # dict<list<any>>Stricter type checkingtype-checkingIn legacy Vim script, wherea number was expected,astring would beautomatically converted toa number.  This was convenient for an actual numbersuchas "123", but leads to unexpected problems (and no error message) if thestring doesn't start witha number.  Quite often this leads to hard-to-findbugs.  For example, in legacy Vimscript this echoes "1":echo 123 == '123'However, if an unintendedspaceis included, "0"is echoed:echo 123 == ' 123'E1206InVim9script this has been made stricter.  In most placesit works justasbefore if the value used matches the expected type.  For example, in bothlegacy Vimscript andVim9script trying to use anything other thanadictionary whenitis required:echo [8, 9]->keys()vim9cmd echo [8, 9]->keys() # E1206: Dictionary requiredE1023E1024E1029E1030E1174E1175E1210E1212However, sometimes there will be an error inVim9 script, which breaksbackwards compatibility.  The following examples illustratevarious placesthis happens.  The legacy Vimscript behavior, which does not fail,is shownfirst.  Itis followed by the error that occurs if the same commandis usedinVim9 script.- Usinga number (except0 or 1) wherea boolis expected:echo v:version ? v:true : v:falsevim9cmd echo v:version ? true : false  # E1023: Using a Number as a...- Usinga number whereastringis expected:echo filter([1, 2], 0)vim9cmd echo filter([1, 2], 0)  # E1024: Using a Number as a String- Not usinga number wherea numberis expected:" In this example, Vim script treats v:false as 0function Not1029()  let b:l = [42] | unlet b:l[v:false]endfunctioncall Not1029() | echo b:lvim9scriptdef E1029(): void  b:l = [42] | unlet b:l[false]enddefE1029()  # E1029: Expected number but got bool- Usingastringasa number:let b:l = [42] | unlet b:l['#'] | echo b:lvim9cmd b:l = [42] | vim9cmd unlet b:l['#']  # E1030: Using a string...- Not usingastring where an argument requiresa string:echo substitute('Hallo', 'a', 'e', v:true)vim9cmd echo substitute('Hallo', 'a', 'e', true)  # E1174: String...- Using an emptystring in an argument that requiresa non-empty string:echo exepath('')vim9cmd echo exepath('') # E1175: Non-empty string required for arg...- Not usinga number whenitis required:echo gettabinfo('a')vim9cmd echo gettabinfo('a')  # E1210: Number required for argument 1- Not usinga bool whenitis required:echo char2nr('¡', 2)vim9cmd echo char2nr('¡', 2)  # E1212: Bool required for argument 2- Not usinga number whena numberis required(E521):let &laststatus='2'vim9cmd &laststatus = '2'- Not usingastring whenastringis required(E928):let &langmenu = 42vim9cmd &langmenu = 42  # E928: String required- ComparingaSpecial with'is' fails in some instances(E1037,E1072):" 1 is echoed because these are both trueecho v:null is v:null && v:none is v:none" 0 is echoed because all these expressions are falseecho v:none is v:null || v:none is 8 || v:true is v:none" All these are errors in Vim9 scriptvim9cmd echo v:null is v:null # E1037: Cannot use 'is' with specialvim9cmd echo v:none is v:none # E1037: Cannot use 'is' with specialvim9cmd echo v:none is v:null # E1037: Cannot use 'is' with specialvim9cmd echo v:none is 8      # E1072: Cannot compare special with numbvim9cmd echo v:true is v:none # E1072: Cannot compare bool with specialNote: Although the last twoVim9script examples above error usingv:none, they returnfalse usingnull (whichis the sameasv:null- seev:null):vim9scriptecho null is 8# falseecho true is null# false- Usingastring wherea boolis required(E1135):echo '42' ? v:true : v:falsevim9cmd echo '42' ? true : false  # E1135: Using a String as a Bool- Usinga boolasa number(E1138):let &laststatus=v:truevim9cmd &laststatus = true- Not usingastring where an argument requiresastring(E1174)echo substitute('Hallo', 'a', 'e', v:true)vim9cmd echo substitute('Hallo', 'a', 'e', true)  # E1174: String...One consequenceis that the item type ofalist ordict given tomap()mustnot change when its typeis either declared or inferred.  For example, thisgives an error inVim9 script, whereas in legacy Vimscriptitis allowed:" legacy Vim script changes s:mylist to ['item 0', 'item 1']let s:mylist = [0, 1]call map(s:mylist, {i -> $"item {i}"})echo s:mylistvim9scriptvar mylist = [0, 1]                # Vim infers mylist is list<number>map(mylist, (i, _) => $"item {i}") # E1012: type mismatch...The error occurs becausemap() tries to modify thelist elements to strings,which conflicts with the declared type.Usemapnew() instead.  It createsa new list, and Vim infers its type ifitis not specified.  Inferred and declared types are shown in this example:vim9scriptvar mylist = [0, 1]var infer = mylist->mapnew((i, _) => $"item {i}")echo [infer, infer->typename()]var declare: list<string> = mylist->mapnew((i, _) => $"item {i}")echo [declare, declare->typename()]The key concept here is,variables with declared or inferred types cannothave the types of the elements within their containers change.  However, type"changes" are allowed for either:-a container literal (not bound toa variable), or-a container wherecopy() ordeepcopy()is used inmethod chaining.Both are demonstrated in this example:vim9script# list literalecho [1, 2]->map((_, v) => $"#{v}")echo [1, 2]->map((_, v) => $"#{v}")->typename()# deepcopy() in a method chainvar mylist = [1, 2]echo mylist->deepcopy()->map((_, v) => $"#{v}")echo mylist->deepcopy()->map((_, v) => $"#{v}")->typename()echo mylistThe reasoning behind this is, whena typeis either declared or inferredand thelistis passed around and changed, the declaration/inferencemustalways hold so that you can rely on the type to match the declared/inferredtype.  For eitheralist literal ora fully copied list, that type safetyisnot needed because the originallistis unchanged (as "echo mylist" shows,above).If the item type was not declared or determined to be "<any>",it will notchange, even if all items later become the same type.  However, whenmapnew()is used, inference means that the newlist will reflect the type(s) present.For example:vim9script# list<any>var mylist = [1, '2']# mixed types, i.e., list<any>echo (mylist, mylist->typename())# ([1, '2'], 'list<any>')mylist->map((_, v) => $"item {v}")# all items are now stringsecho (mylist, mylist->typename())# both strings, but list<any># mapnew()var newlist = mylist->mapnew((_, v) => v)echo (newlist, newlist->typename())# newlist is a list<string>Usingextend() andextendnew()is similar, i.e.,alist literal may usethe former, so, thisis okay:vim9cmd echo [1, 2]->extend(['3'])# [1, 2, 3]whereas, thisis not:vim9scriptvar mylist: list<number> = [1, 2]echo mylist->extend(['3'])   # E1013: Argument 2: type mismatchUsingextendnew()is needed for extending an existing typed list, exceptwhere the extension matches the list's type (oritis "any").  For example,first extending with an element of the same type, then extending withadifferent type:vim9scriptvar mylist: list<number> = [1, 2]mylist->extend([3])echo mylist->extendnew(['4'])   # [1, 2, 3, '4']E1158Usingflatten()is not allowed inVim9 script, becauseitis intendedalways to change the type.  This even applies toalist literal(unlikemap() andextend()).  Instead, useflattennew():vim9cmd [1, [2, 3]]->flatten()# E1158: Cannot use flattenvim9cmd echo [1, [2, 3]]->flattennew()# [1, 2, 3]Assigning toa funcref with specified arguments (seevim9-func-declaration)involves strict type checking of the arguments.  For example, this works:vim9scriptvar F_name_age: func(string, number): stringF_name_age = (n: string, a: number): string => $"Name: {n}, Age: {a}"echo F_name_age('Bob', 42)whereas this fails with errorE1012 (type mismatch):vim9scriptvar F_name_age: func(string, number): stringF_name_age = (n: string, a: string): string => $"Name: {n}, Age: {a}"If thereisa variable number of arguments theymust have the same type,as inthis example:vim9scriptvar Fproduct: func(...list<number>): numberFproduct = (...v: list<number>): number => reduce(v, (a, b) => a * b)echo Fproduct(3, 2, 4)  # Echoes 24And<any> may be used to accommodate mixed types:vim9scriptvar FlatSort: func(...list<any>): anyFlatSort = (...v: list<any>) => flattennew(v)->sort('n')echo FlatSort(true, [[[5, 3], 2], 4])  # Echoes [true, 2, 3, 4, 5]Note: Using<any> inalambda does not avoid type checking of thefuncref.  It remains constrained by the declared funcref'stype and,as these examples show,a runtime or compiling erroroccurs when the types mismatch:    vim9script    var FuncSN: func(string): number    FuncSN = (v: any): number => v->str2nr()    echo FuncSN('162')->nr2char()  # Echoes ¢    echo FuncSN(162)->nr2char())   # E1013 (runtime error)    vim9script    var FuncSN: func(string): number    FuncSN = (v: any): number => v->str2nr()    def FuncSNfail(): void        echo FuncSN('162')->nr2char()  # No echo because ...        echo FuncSN(162)->nr2char()    # Error while compiling    enddef    FuncSNfail()When the funcref has no arguments specified, thereis no type checking.  Thisexample shows FlexArgs hasastring argument the first time andalist thefollowing time:vim9scriptvar FlexArgs: func: stringFlexArgs = (s: string): string => $"It's countdown time {s}..."echo FlexArgs("everyone")FlexArgs = (...values: list<string>): string => join(values, ', ')echo FlexArgs('3', '2', '1', 'GO!')E1211E1217E1218E1219E1220E1221E1222E1223E1224E1225E1226E1228E1235E1238E1251E1253E1256E1297E1298E1301E1528E1529E1530E1531E1534Types are checked for most builtinfunctions to makeit easier to spotmistakes.  The following one-line:vim9 commands, calling builtin functions,demonstrate many of thosetype-checking errors:vim9 9->list2blob()               # E1211: List required for argume...vim9 9->ch_close()                # E1217: Channel or Job required ...vim9 9->job_info()                # E1218: Job required for argumen...vim9 [9]->cos()                   # E1219: Float or Number required...vim9 {}->remove([])               # E1220: String or Number require...vim9 null_channel->ch_evalraw(9)  # E1221: String or Blob required ...vim9 9->col()                     # E1222: String or List required ...vim9 9->complete_add()            # E1223: String or Dictionary req...vim9 setbufline(9, 9, {})         # E1224: String, Number or List r...vim9 9->count(9)                  # E1225: String, List, Tuple or D...vim9 9->add(9)                    # E1226: List or Blob required fo...vim9 9->remove(9)                 # E1228: List, Dictionary, or Blo...vim9 getcharstr('9')              # E1235: Bool or number required ...vim9 9->blob2list()               # E1238: Blob required for argume...vim9 9->filter(9)                 # E1251: List, Tuple, Dictionary,...vim9 9->reverse()                 # E1253: String, List, Tuple or B...vim9 9->call(9)                   # E1256: String or Function requi...vim9 null_dict->winrestview()     # E1297: Non-NULL Dictionary requ...vim9 {}->prop_add_list(null_list) # E1298: Non-NULL List required f...vim9 {}->repeat(9)                # E1301: String, Number, List, Tu...vim9 9->index(9)                  # E1528: List or Tuple or Blob re...vim9 9->join()                    # E1529: List or Tuple required f...vim9 9->max()                     # E1530: List or Tuple or Diction...vim9 9->get(9)                    # E1531: Argument of get() must b...vim9 9->tuple2list()              # E1534: Tuple required for argum...Reserved for future use:E1227E1250E1252E1227:List orDictionary required for argument %dE1250: Argument of %smust bea List, String,Dictionary orBlobE1252: String,List orBlob required for argument %dCategories of variables, defaults and null handlingvariable-categoriesnull-variablesThere are three categories of variables:primitivenumber, float,booleancontainerstring, blob, list, tuple,dictspecializedfunction, job, channel, user-defined-objectWhen declaringa variable without an initializer, an explicit typemust beprovided.  Each category has different defaultinitialization semantics.Primitives default to type-specific values.  All primitives are empty butdonot equalnull:    vim9script    var n: number | echo [n, n->empty(), n == null]  # [0, 1, false]    var f: float  | echo [f, f->empty(), f == null]  # [0.0, 1, false]    var b: bool   | echo [b, b->empty(), b == null]  # [false, 1, false]Containers default to an empty container.  Only an emptystring equalsnull:    vim9script    var s: string       | echo [s, s->empty(), s == null]  # ['', 1, true]    var z: blob         | echo [z, z->empty(), z == null]  # [0z, 1, false]    var l: list<string> | echo [l, l->empty(), l == null]  # [[], 1, false]    var t: tuple<any>   | echo [t, t->empty(), t == null]  # [(), 1, false]    var d: dict<number> | echo [d, d->empty(), d == null]  # [{}, 1, false]Specialized types default to equalingnull:    vim9script    var F: func    | echo [F, F == null]  # [function(''), true]    var j: job     | echo [j, j == null]  # ['no process', true]    var c: channel | echo [c, c == null]  # ['channel fail', true]    class Class    endclass    var o: Class   | echo [o, o == null]  # [object of [unknown], true]    enum Enum    endenum    var e: Enum    | echo [e, e == null]  # [object of [unknown], true]Note: Seeempty() for explanations of empty job, empty channel, andemptyobject types.Vim does not havea familiarnull value.  Instead,it hasvarious null_<type>predefined values includingnull_string,null_list, andnull_job.Primitivesdo not havea null_<type>.  Typical use cases for null_<type> are:- to cleara variable and release its resources,-asa default fora parameter ina function definition (for an example,      seenull_blob), or- assigned toa container or specialized variable to setit tonull      for later comparison (for an example, seenull-compare).Fora specialized variable, likejob, null_<type>is used to clear theresources.  For example:vim9scriptvar mydate: list<string>def Date(channel: channel, msg: string): void    mydate->add(msg)enddefvar myjob = job_start([&shell, &shellcmdflag, 'date'], {out_cb: Date})echo [myjob, myjob->job_status()]sleep 2echo $"The date and time is {mydate->join('')}"echo [myjob, myjob->job_status()]myjob = null_job  # Clear the variable; release the job's resources.echo myjobFora container variable, resources may also be cleared by assigning anempty container to the variable.  For example:vim9scriptvar perfect: list<number> = [1, 4]perfect->extend([9, 16, 25])perfect = []echo perfectUsing an empty container, rather than null_<type>, to cleara containervariable may avoidnull complications- seenull-anomalies.Theinitialization semantics of containervariables and specializedvariablesdiffer.  For containers:- An uninitialized container defaults to empty but does not equalnull    (except fora uninitialized string).-A container initialized to [], (), {}, "", or 0zis empty but does not    equalnull.-A container initializedas null_<type> defaults to empty and equalsnull.In the following example, the uninitializedlist("lu") and[] initializedlist("li") are equivalent and indistinguishable whereas "ln"isanullcontainer, whichis similar to, but not equivalent to, an empty container(seenull-anomalies).vim9script# uninitialized: empty container, not nullvar lu: list<any>echo ['lu', $"empty={lu->empty()}", $"null={lu == null}"]# initialized: empty container, not nullvar li: list<any> = []echo ['li', $"empty={li->empty()}", $"null={li == null}"]# initialized: empty container, nullvar ln: list<any> = null_listecho ['ln', $"empty={ln->empty()}", $"null={ln == null}"]Specializedvariables default to equaling null.  Thesejob initializationsare equivalent and indistinguishable:vim9scriptvar j1: jobvar j2: job = null_jobvar j3 = null_jobecho (j1 == j2) == (j2 == j3)  # true (equivalent, indistinguishable)Whena list, tuple, ordictis declared, if the item typeis not specifiedit cannot be inferred.  Consequently, the item type defaults to "any":vim9scriptvar [t1, t2] = [(), null_tuple]echo $'t1 is {t1->typename()} and t2 is {t2->typename()} too'Tuples andfunctions (or partials) may be declared invarious ways.Seetuple-type,variadic-tuple, andvim9-func-declaration.null-compareFor familiarnull compare semantics, where an empty containeris not equal toanull container,do not use null_<type> ina comparison.  Thatis because,inVim9 script, although null_<type>==null, comparing an:- empty container tonullisfalse, but- empty container to null_<type>istrue.So, compare againstnull, not null_<type>.  For example:vim9scriptvar bonds: dict<list<string>> = {g: ['007', '008'], o: ['007', '009']}def Search(query: string): list<string>    return query == "\r" ? null_list : bonds->get(query, [])enddefecho "Goldfinger (g) or Octopussy (o)?: "const C: string = getcharstr()var result: list<string> = C->Search()if result == null  # <<< DO NOT USE null_list HERE!    echo "Error: Nothing was entered"else    echo result->empty() ? $"No matches for '{C}'" : $"{result}"endifNOTE: Using "result==null_list" instead of "result==null" wouldfail to distinguish the error (nothing entered) and the valid(nothing matched) result because[]==null_list whereas[] != null.Conceptually, think of the null_<type> constructasa hybrid/bridge betweenthe generalnull and typedempty containers, having properties of both.In the followingsection there are details about comparison results.null-detailsnull-anomaliesThissection describes issues about usingnull and null_<type>; included beloware the enumerated results ofnull comparisons.  In some cases, if familiarwithvim9null semantics, the programmer may choose to use null_<type> incomparisons and/or other situations.Elsewhere in the documentationit says, "oftenanull valueis handled thesameas an empty value, but not always".  For example, you cannot add toanull container:vim9scriptvar le: list<any> = []le->add('Okay')# le is now ['Okay']var ln = null_listln->add("E1130")# E1130: Cannot add to null listAs explained innull-compare, thereisa non-transitive relationship amongnull, null_<type> containers, andempty.  To recap, for example:vim9cmd echo (null_dict == {}, null_dict == null, {} != null)The exceptionis an uninitialized string.  Itis equal tonull (andis thesame instanceasnull_string).  The'is'operator(expr-is) may be used todetermine whetherastringis uninitialized:vim9scriptvar s: stringecho s == null_string# trueecho s is null_string# true (the same instance)echo s == null# true (unexpected, perhaps)echo s is null# false (not the same instance)However, don'tdo the same for the other containers because, when evaluatedagainst their applicable null_<type> with'is', they returnfalse:vim9scriptvar d: dict<any>echo d == null_dict# trueecho d is null_dict# false (not the same instance)echo d == null# false (as expected)echo d is null# false (not the same instance)The key distinction hereis an uninitializedstringis implementedasnull_string, while an uninitialized list, dict, tuple, orblobisimplementedas an empty container ([], {}, (), and 0z respectively).So, those uninitialized types are equal to, but not the same instance as,their null_<type> counterparts,as this example shows:vim9scriptvar t: tuple<any>echo t == null_tuple# trueecho t is null_tuple# falseHowever,a variable initialized to the null_<type>is equal not only to thenull_<type>,itis also equal to null.  For example:vim9scriptvar t: tuple<any> = null_tupleecho t == null_tuple# trueecho t is null_tuple# trueecho t == null# trueAn uninitialized container variableis not equal to null, except for anuninitialized string, whichis explained in an example, above.  So, theseall echotrue:vim9scriptvar b: blob| echo b != nullvar d: dict<any>| echo d != nullvar l: list<any>| echo l != nullvar t: tuple<any>| echo t != nullvar s: string| echo s == nullAn uninitialized specialized variableis equal tonull so these all echotrue:vim9scriptvar c: channel| echo c == nullvar F: func| echo F == nullvar j: job| echo j == nullclass Classendclassvar nc: Class| echo nc == nullenum Enumendenumvar ne: Enum| echo ne == nullNote: the specialized variables, like job, default tonull andno specialized variable hasa corresponding empty value.A container variable initialized to empty equals null_<type>, so these are alltrue:vim9scriptvar s: string = ""     | echo s == null_stringvar b: blob = 0z       | echo b == null_blobvar l: list<any> = []  | echo l == null_listvar t: tuple<any> = () | echo t == null_tuplevar d: dict<any> = {}  | echo d == null_dictHowever,a container variable initialized to empty does not equal null, sothese are alltrue:vim9scriptvar s: string = ""     | echo s != nullvar b: blob = 0z       | echo b != nullvar l: list<any> = []  | echo l != nullvar t: tuple<any> = () | echo t != nullvar d: dict<any> = {}  | echo d != null==============================================================================generic-functions5. GenericfunctionsA generic function allows using the same function with different typearguments, while retaining type checking for arguments and the return value.This provides type safety and code reusability.Declarationgeneric-function-declarationE1553E1554The typevariables fora generic function are declaredas its type parameterswithin angle brackets("<" and ">"), directly after the function name.Multiple type parameters are separated by commas:def[!] {funcname}<{type} [, {types}]>([arguments])[: {return-type}]    {function body}enddefgeneric-function-exampleThese type parameters may then be used, like any other type, within thefunction signature and its body.  The following example combines two listsintoalist of tuples:vim9scriptdef 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 resultenddefvar 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-namingE1552type-parameter-namingAs in the preceding example, the conventionis to usea single capitalletterfora name (e.g., T, U, A, etc.).  Although they may comprise more than oneletter, namesmust start witha capital letter.  In this example, "Ok"isvalid whereas "n"is not:vim9scriptdef MyFail<Ok, n>(): voidenddef# E1552: Type variable name must start with an uppercase letter: n...E1558E1560A functionmust be declared and used eitherasa generic function orasaregular function, but not both.  The followingVim9 scripts demonstrate theseerrors:vim9scriptMy1558<number>()# E1558: Unknown generic function: My1558vim9scriptdef My1560(): voidenddefMy1560<string>()# E1560: Not a generic function: My1560E1561Type parameter namesmust not clash with other identifiers:vim9scriptdef My1561<D, E, D>(): Denddef# E1561: Duplicate type variable name: Dvim9scriptenum E  Yes, Noendenumdef My1041<E>(): Eenddef# E0141: Redefining script item "E"Calling a generic functiongeneric-function-callTo calla generic function, specify the concrete types in "<" and ">"between the function name and the argument list:MyFunc<number, string, list<number>>()NOTE: There are several working examples in this section, which may      be sourced, includinggeneric-function-example.E1555E1556E1557E1559The number of passed type arguments to the functionmust match the numberof its declared type parameters.  An empty typelistis not allowed.Examples:vim9scriptdef My1555<>(): voidenddef# E1555: Empty type list specified for generic function ...vim9scriptdef My1556<T>(): voidenddefMy1556<bool, bool>()# E1556: Too many types specified for generic function ...vim9scriptdef My1557<T, U>(): voidenddefMy1557<bool>()# E1557: Not enough types specified for generic function ...vim9scriptdef My1559<T>(): TenddefMy1559()# Vim(eval):E1559: Type arguments missing for generic function ...AnyVim9 type(vim9-types) can be usedasa concrete type ina genericfunction.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 likea regular function.See:export and:import.A generic function can be defined inside another regular or generic function.Example:vim9scriptdef 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 42enddefOuter()Using a type variable as a type argumentA type variable may also be passedasa type argument.  For example:vim9script# T is declared as a type parameter# It is used for the 'value' parameter and the return typedef Id<T>(value: T): T    return valueenddef# U is declared as a type parameter# It is used for the 'value' parameter and the return typedef CallId<U>(value: U): U    # U is a type variable passed/used as a type argument    return Id<U>(value)enddefecho CallId<string>('I am') .. ' ' .. CallId<number>(42)Thisis useful for complex data structures like dictionaries of lists or,as in the following example, lists of dictionaries:vim9scriptdef Flatten<T>(x: list<list<T>>): list<T>    final result: list<T> = []    for inner in x        result->extend(inner)    endfor    return resultenddefconst 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])In "Flatten<T>", "T"isa declared type parameter.  Everywhere else inthe function, "T"isa type variable referencing that type parameter.Generic class methodAVim9classmethod can bea generic function:vim9scriptclass Config    var settings: dict<any>    def Get<T>(key: string): T        return this.settings[key]    enddefendclassvar c: Config = Config.new({timeout: 30, debug: true})echo c.Get<number>('timeout')echo c.Get<bool>('debug')E1432E1433E1434A genericclassmethod ina baseclass can be overridden bya genericmethodina child class.  The number of typevariablesmust match between bothmethods.A concreteclassmethod cannot be overridden bya generic method,and vice versa.Generic function referenceA functionreference(Funcref) can bea generic function.  This allows forcreating factories offunctions that operate on specific types: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<string, string> = (c, c->char2nr()->string())def Matcher<T>(pattern: string): func(T): bool    return (value: T): bool => match(value, pattern) >= 0enddefvar StringMatch = Matcher<string>(char_dec[0])echo "*+"->StringMatch()    # true (has *)echo ",-"->StringMatch()    # falsevar ListMatch = Matcher<list<number>>(char_dec[1])echo [42, 43]->ListMatch()    # true (has 42)echo [44, 45]->ListMatch()    # falseCompiling and Disassembling Generic functionsThe:defcompile command can be used to compilea generic function withaspecificlist of concrete types:defcompile MyFunc<number, list<number>, dict<string>>The:disassemble command can be used tolist the instructions generated fora generic function:disassemble MyFunc<string, dict<string>>disassemble MyFunc<number, list<blob>>Limitations and Future WorkCurrently, Vim does not support:- Type inference for type variables: All typesmust be explicitly specified     when callinga generic function.- Type constraints: It's not possible to restricta type variable toa     specificclass orinterface (e.g., `Textends SomeInterface`).- Default type arguments: Providinga default type fora type parameter     when not explicitly specified.==============================================================================6. Namespace, Import and Exportvim9scriptvim9-exportvim9-importAVim9script can be written to be imported.  This means that some items areintentionally exported, made available to other scripts.  When the exportingscriptis imported in another script, these exported items can then be used inthat script.  All the other items remainscript-local in the exportingscriptand cannot be accessed by the importing script.This mechanism exists forwritingascript that can be sourced (imported) byother scripts, while making sure these other scripts only have access to whatyou want them to.  This also avoids using the global namespace, which hasarisk of name collisions.  For example when you have two plugins with similarfunctionality.You can cheat by using the global namespace explicitly.  That should be doneonly for things that really are global.Namespacevim9-namespaceTo recognizea file that can be imported thevim9script statementmustappearas the first statement in the file (seevim9-mix for an exception).It tells Vim to interpret thescript in its own namespace, instead of theglobal namespace.  Ifa file starts with:vim9scriptvar myvar = 'yes'Then "myvar" will only exist in this file.  While withoutvim9scriptit wouldbe availableasg:myvar from any otherscript and function.E1101Thevariablesat the file level are very much like thescript-local "s:"variables in legacy Vim script, but the "s:"is omitted.  And they cannot bedeleted.InVim9script the global "g:" namespace can still be usedas before.  And the"w:", "b:" and "t:" namespaces.  These have in common thatvariables are notdeclared, have no specific type and they can be deleted.E1304A side effect of:vim9scriptis that the'cpoptions' optionis set to theVim default value, like with::set cpo&vimOne of the effectsis thatline-continuationis always enabled.The original value of'cpoptions'is restoredat theend of the script, whileflags added or removed in thescript are also added to or removed from theoriginal value to get the same effect.  The order of flags may change.In thevimrc file sourced onstartup this does not happen.vim9-mixThereis one way to use both legacy andVim9syntax in onescript file:" _legacy Vim script_ comments hereif !has('vim9script')   " _legacy Vim script_ comments/commands here   finishendifvim9script# _Vim9 script_ commands/commands from here onwardsechowindow $"has('vim9script') == {has('vim9script')}"This allows forwritingascript that takes advantage of theVim9scriptsyntax if possible, and prevents thevim9script command from throwing anerror if used ina version of Vim without 'vim9script'.Note thatVim9syntax changed before Vim 9 so that scripts using the currentsyntax (suchas "import from" instead of "import") might throw errors.To prevent these,a safer check may bev:version >= 900 instead (because"has('vim9script')" will returnv:true back to Vim 8.2 with patch 3965).Sometimesitis prudent to cut off even later.Vim9 script's feature setcontinues to grow so, for example, if tuples are used (introduced in Vim 9.1patch 1232),a better condition is:if !has('patch-9.1.1232')  echowindow $"Fail: Vim does not have patch 9.1.1232"  finishendifvim9scriptechowindow $"Pass: version {v:versionlong}.  Continuing ..."Whichever vim-mix conditionis used,it only works in one of two ways:    1. The "if" statement evaluates to false, the commands up toendif are       skipped andvim9scriptis then the first command actually executed.    2. The "if" statement evaluates to true, the commands up toendif are       executed andfinish bails out before reachingvim9script.Export:export:expExporting an item can be written as:export const EXPORTED_CONST = 1234export var someValue = ...export final someValue = ...export const someValue = ...export def MyFunc() ...export class MyClass ...export interface MyClass ...export enum MyEnum ...E1043E1044As this suggests, only constants, variables,:def functions, classes,interfaces and enums can be exported.E1042:export can only be used inVim9 script,at thescript level.Import:import:impE1094E1047E1262E1048E1049E1053E1071E1088E1236The exported items can be imported in another script.  The importsyntax hastwo forms.  The simple form:import {filename}Where{filename}is anexpression thatmust evaluate toa string.  In thisform the filename shouldend in ".vim" and the portion before ".vim" willbecome thescript local name of the namespace.  For example:import "myscript.vim"This makes each exported item in "myscript.vim" availableas "myscript.item".:import-asE1257E1261Incase the nameis long or ambiguous, this form can be used to specifyanother name:import {longfilename} as {name}In this form{name} becomesa specificscript local name for the importednamespace.  Therefore{name}must consist of letters, digits and '_', likeinternal-variables.  The{longfilename}expressionmust evaluate to anyfilename.  For example:import "thatscript.vim.v2" as thatE1060E1258E1259E1260Then you can use "that.item", etc.  You are free to choose the name "that".Use something that will be recognizedas referring to the imported script.Avoid command names, command modifiers and builtin function names, because thename will shadow them.  It's better not to start the name witha capitalletter, sinceit can then also shadow global user commands and functions.Also, you cannot use the name for something else in the script, suchasafunction or variable name.Incase the dot in the nameis undesired,a localreference can be made forafunction:var LongFunc = that.LongFuncNameThis also works for constants:const MAXLEN = that.MAX_LEN_OF_NAMEThis does not work for variables, since the value would be copied once andwhenchanging the variable the copy will change, not the original variable.You will need to use the full name, with the dot.:import can not be used ina function.  Imported items are intended to existat thescript level and only imported once.Thescript name afterimport can be:-A relative path,starting "." or "..".  This findsa file relative to the  location of thescript file itself.  Thisis useful to split upa largeplugin into several files.- An absolute path,starting with "/" onUnix or "D:/" on MS-Windows.  This  will rarely be used.-A path not being relative or absolute.  This will be found in the  "import" subdirectories of'runtimepath' entries.  The name will usually be  longer and unique, to avoid loading the wrong file.Note that "after/import"is not used.If the name does notend in ".vim" then the use of "as name"is required.OnceaVim9script file has been imported, the resultis cached and used thenext time the samescriptis imported.  It will not be read again.Itis not allowed to import the samescript twice, also when using twodifferent "as" names.When using the imported name the dot and the item namemust be in the sameline, there can be no line break:echo that.name   # Error!echo that.name  # Error!import-mapWhen you've importeda function from onescript intoaVim9script you canrefer to the imported function inamapping by prefixingit with<SID>:noremap <silent> ,a :call <SID>name.Function()<CR>When themappingis defined "<SID>name." will be replaced with<SNR> and thescript ID of the imported script.An even simpler solutionis using<ScriptCmd>:noremap ,a <ScriptCmd>name.Function()<CR>Note that this does not work for variables, only for functions.import-legacylegacy-import:import can also be used in legacy Vim script.  The imported namespace stillbecomes script-local, even when the "s:" prefixis not given.  For example:import "myfile.vim"call s:myfile.MyFunc()And using the "as name" form:import "otherfile.vim9script" as thatcall s:that.OtherFunc()However, the namespace cannot be resolved on its own:import "that.vim"echo s:that" ERROR: E1060: Expected dot after name: s:thatThis also affects the use of<SID> in the legacymapping context.  Since<SID>is onlya valid prefix fora function and NOT fora namespace, youcannot useit to scopea function inascript local namespace.  Instead ofprefixing the function with<SID> you should use<ScriptCmd>.  For example:noremap ,a <ScriptCmd>:call s:that.OtherFunc()<CR>:import-cycleTheimport commands are executed when encountered.  IfscriptA importsscript B, andB (directly or indirectly) imports A, this will be skipped over.At this point items inA after "importB" will not have been processed anddefined yet.  Therefore cyclic imports can exist and not result in an errordirectly, but may result in an error for items inA after "importB" not beingdefined.  This does not apply toautoload imports, see the next section.Importing an autoload scriptvim9-autoloadimport-autoloadFor optimalstartup speed, loading scripts should be postponed until they areactually needed.  Using theautoload mechanismis recommended:E1264     1. In the plugin, define user commands,functions and/or mappings        referring to items imported from anautoload script.import autoload 'for/search.vim'command -nargs=1 SearchForStuff search.Stuff(<f-args>)        This goes in .../plugin/anyname.vim.  "anyname.vim" can be freely        chosen.  The "SearchForStuff" commandis now available to the user.        The "autoload" argument to:import means that thescriptis not        loaded until one of the itemsis actually used.  Thescript will be        found under the "autoload" directory in'runtimepath' instead of the        "import" directory.  Alternatively, eithera relative or absolute        name can be used- see below.     2. In theautoloadscriptput the bulk of the code.vim9scriptexport def Stuff(arg: string): void  ...       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 prefixis obtained from the file name, justas you would       addit manually ina legacyautoload 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 theautoloadscript when compilinga function that       encounters this name).       You can split up the functionality and import other scripts from theautoloadscriptas you like.  This way you can share code between       plugins.Searching for theautoloadscript in all entries in'runtimepath' can bea bitslow.  If theplugin knows where thescriptis located, quite oftena relativepath can be used.  This avoids the search and should be quitea bit faster.Another advantageis that thescript name does not need to be unique.  Also,an absolute pathis possible.  Examples:import autoload '../lib/implement.vim'import autoload MyScriptsDir .. '/lib/implement.vim'For definingamapping that uses the importedautoloadscript the special key<ScriptCmd>is useful.  It allows fora command inamapping to use thescript context of where themapping was defined.When compilinga:def function anda function in anautoloadscriptisencountered, thescriptis not loaded until the:def functionis called.This also means you get anyerrors onlyat runtime, since the argument andreturn types are not known yet.  If you would use the name with '#' charactersthen theautoloadscript IS loaded.Be careful to not refer to an item in anautoloadscript that does triggerloadingit unintentionally.  For example, when setting an option that takesafunction name, make sure to usea string, nota function reference:import autoload 'qftf.vim'&quickfixtextfunc = 'qftf.Func'  # autoload script NOT loaded&quickfixtextfunc = qftf.Func    # autoload script IS loadedOn the other hand,it can be useful to load thescript early,ata time whenanyerrors should be given.Fortesting thetest_override() function can be used to have the`import autoload` load thescript right away, so that the items and types canbe checked without waiting for them to be actually used:test_override('autoload', 1)Resetit later with:test_override('autoload', 0)Or:test_override('ALL', 0)==============================================================================7. Classes and interfacesvim9-classesIn legacy Vim script,aDictionary could be usedasa kind-ofobject by addingmembers that are functions.  However, thisis quite inefficient and requiresthe writer todo the work of making sure all theobjects have the rightmembers.  SeeDictionary-function.InVim9script you can have classes, objects, interfaces, and enums likein most popular object-oriented programming languages.  Since thisisa lotof functionality,itis located ina separatehelp file:vim9class.txt.==============================================================================8. Rationalevim9-rationaleThe :def commandPlugin writers have asked for much faster Vim script.  Investigations haveshown that keeping the existing semantics of function calls make this close toimpossible, because of the overhead involved with callinga function, settingup the local function scope and executing lines.  There are many details thatneed to be handled, suchas errormessages and exceptions.  The need to createa dictionary for a: andl: scopes, thea:000list and several others add toomuch overhead that cannot be avoided.Therefore the:defmethod to definea new-style function had to be added,which allows fora function with different semantics.  Most things still workas before, but some partsdo not.A new way to definea function wasconsidered the best way to separate the legacy style code fromVim9 stylecode.Using "def" to definea function comes from Python.  Other languages use"function" which clashes with legacy Vim script.Type checkingWhen compiling lines of Vim commands into instructionsas muchas possibleshould be doneat compile time.  Postponingit to runtime makes the executionslower and means mistakes are found only later.  For example, whenencountering the "+" character and compiling this intoa generic addinstruction,at runtime the instruction would have to inspect the type of thearguments and decide what kind of addition to do.  And when the typeisdictionary throw an error.  If the types are known to be numbers then an "addnumber" instruction can be used, whichis faster.  The error can be givenatcompile time, no error handlingis neededat runtime, since adding two numbersalmost never fails.NOTE: Asa tangential point, the exceptionis integer overflow, where the    result exceeds the maximum integer value.  For example, adding toa 64-bit    signed integer where the resultis greater than 2^63:vim9scriptecho 9223372036854775807 + 1     # -9223372036854775808echo 2->pow(63)->float2nr() + 1  # -9223372036854775808Thesyntax for types, using<type> for compound types,is similar to Java.Itiseasy to understand and widely used.  The type names are what were usedin Vim before, with some additions suchas "void" and "bool".Removing clutter and weirdnessOnce decided that:deffunctions have differentsyntax than legacy functions,we are free to add improvements to make the code more familiar for users whoknow popular programming languages.  In other words: remove weird things thatonly Vim does.We can also remove clutter, mainly things that were done to make Vimscriptbackwards compatible with the good oldVi commands.Examples:- Drop:call for callinga function and:eval for evaluating an  expression.- Drop usinga leadingbackslash for line continuation, automatically figure  out where anexpression ends.However, this does require that some things need to change:- Comments start with# instead of ", to avoid confusing them with strings.  Thisis good anyway,itis also used by several popular languages.-Ex command ranges need to be prefixed witha colon, to avoid confusion with  expressions (singlequote can beastring ora mark, "/" can be divide ora  search command, etc.).Goalis to limit the differences.A good criteriais that when the oldsyntaxis accidentally used you are very likely to get an error message.Syntax and semantics from popular languagesScript writers have complained that the Vimscriptsyntaxis unexpectedlydifferent from what they are used to.  To reduce this complaint popularlanguages are usedas an example.  At the same time, wedo not want toabandonthe well-known parts of legacy Vim script.For many things TypeScriptis followed.  It'sa recent language thatisgaining popularity and has similarities with Vim script.  It also hasamix of static typing (a variable always hasa known value type) and dynamictyping (a variable can have different types, this changesat runtime).  Sincelegacy Vimscriptis dynamically typed anda lot of existing functionality(esp. builtin functions) depends on that, while static typing allows for muchfaster execution, we need to have this mix inVim9 script.Thereis no intention to completely match TypeScriptsyntax and semantics.  Wejust want to take those parts that we can use for Vim and we expect Vim userswill be happy with.  TypeScriptisa complex language with its own history,advantages and disadvantages.  To get an idea of the disadvantages read thebook: "#".  Or find the article "TypeScript: the goodparts" and read the "Things to avoid" section.People familiar with other languages (Java, Python, etc.) will also findthings in TypeScript that theydo not like ordo not understand.  We'll try toavoid those things.Specific items from TypeScript we avoid:- Overloading "+", usingit both for addition andstring concatenation.  This  goes against legacy Vimscript and often leads to mistakes.  For that reason  we will keep using ".." forstring concatenation.Lua also uses ".." this  way.  Andit allows for conversion tostring for more values.- TypeScript can use anexpression like "99 ||'yes'" ina condition, but  cannot assign the value toa boolean.  Thatis inconsistent and can be  annoying.  Vim recognizes anexpression with && or || and allows using the  resultasa bool.  Thefalsy-operator was added for the mechanism to usea  default value.- TypeScript considers an emptystringas Falsy, but an emptylist ordictas  Truthy.  Thatis inconsistent.  In Vim an emptylist anddict are also  Falsy.- TypeScript hasvarious "Readonly" types, which have limited usefulness,  sincea type cast can remove the immutable nature.  Vim locks the value,  whichis more flexible, butis only checkedat runtime.- TypeScript hasa complicated "import" statement that does not match how the  Vim import mechanism works.A much simpler mechanismis used instead, which  matches that the importedscriptis only sourced once.DeclarationsLegacy Vimscript uses:let for every assignment, while inVim9 declarationsare used.  Thatis different, thus it's good to usea different command::var.  Thisis used in many languages.  The semantics might be slightlydifferent, but it's easily recognizedasa declaration.Using:const  for constantsis common, but the semantics varies.  Somelanguages only make the variable immutable, others also make the valueimmutable.  Since "final"is well known from Java for only making the variableimmutable we decided to use that.  And then:const can be used for makingboth immutable.  This was also used in legacy Vimscript and the meaningisalmost the same.What weend up withis very similar to Dart::var name# mutable variable and value:final name# immutable variable, mutable value:const name# immutable variable and valueSince legacy andVim9script will be mixed and globalvariables will beshared, optional type checkingis desirable.  Also, type inference will avoidthe need for specifying the type in many cases.  The TypeScriptsyntax fitsbest for adding types to declarations:var name: string  # string type is specified...name = 'John'const greeting = 'hello'  # string type is inferredThisis how weput types ina declaration:var mylist: list<string>final mylist: list<string> = ['foo']def Func(arg1: number, arg2: string): boolTwo alternatives were considered:    1. Put the type before the name, like Dart:var list<string> mylistfinal list<string> mylist = ['foo']def Func(number arg1, string arg2) bool    2. Put the type after the variable name, butdo not usea colon, like Go:var mylist list<string>final mylist list<string> = ['foo']def Func(arg1 number, arg2 string) boolThe firstis more familiar for anyone used toC or Java.  The second onedoesn't really have an advantage over the first, so let'sdiscard the second.Since we use type inference the type can be left out whenit can be inferredfrom the value.  This means that aftervar we don't know ifa type ora namefollows.  That makes parsing harder, not only for Vim but also for humans.Also,it will not be allowed to usea variable name that could bea type name,using `varstring string`is too confusing.The chosen syntax, usinga colon to separate the name from the type, addspunctuation, butit actually makesit easier to recognize the parts ofadeclaration.ExpressionsExpression evaluation was already close to what other languages are doing.Some details are unexpected and can be improved.  For exampleabooleancondition would accepta string, convertit toa number and check if thenumberis non-zero.  Thisis unexpected and often leads to mistakes, sincetext notstarting witha number would be converted to zero, whichisconsidered false.  Thus usingastring fora condition would often not give anerror and be considered false.  Thatis confusing.InVim9 type checkingis stricter to avoid mistakes.  Wherea conditionisused, e.g. with the:if command and the|| operator, only boolean-likevalues are accepted:true:true,v:true,1,`0< 9`false:false,v:false,0,`0> 9`Note that the number zeroisfalse and the number oneis true.  Thisis morepermissive than most other languages.  It was done because many builtinfunctions return these values, andchanging that causes more problems thanitsolves.  After using this fora whileit turned out to work well.If you have any type of value and want to useitasa boolean, use the!!operator (seeexpr-!):vim9script# The following are all true:echo [!!'text', !![1], !!{'x': 1}, !!1, !!1.1]# And these are all false:echo [!!'', !![], !!{}, !!0, !!0.0]Froma language like JavaScript we have this handy construct:GetName() || 'unknown'However, this conflicts with only allowingaboolean fora condition.Therefore the "??"operator was added:GetName() ?? 'unknown'Here you can explicitly express your intention to use the value as-is and notresult ina boolean.  Thisis called thefalsy-operator.Import and ExportA problem of legacy Vimscriptis that by default allfunctions andvariablesare global.  Itis possible to make them script-local, but then they are notavailable in other scripts.  This defies the concept ofa package that onlyexports selected items and keeps the rest local.InVim9scripta mechanism very similar to the JavaScript import and exportmechanismis supported.  Itisa variant to the existing:source commandthat works like one would expect:- Instead of making everything global by default, everythingis script-local,  some of these are exported.- When importingascript the symbols that are imported are explicitly listed,  avoiding name conflicts and failures if functionalityis added later.- The mechanism allows forwritinga big, longscript witha very clear API:  the exported functions,variables and classes.- By using relative paths loading can be much faster for an import inside ofa  package, no need to search many directories.- Once an import has been used, its items are cached and loadingit againis  not needed.- The Vim-specific use of "s:" to make thingsscript-local can be dropped.When sourcingaVim9script (from eitheraVim9script or legacy Vim script),only the items defined globally can be used, not the exported items.Alternatives considered:- All the exported items become availableasscript-local items.  This makesit uncontrollable what items get defined and likely soon leads to trouble.- Use the exported items and make them global.  Disadvantageis that it's then  not possible to avoid name clashes in the global namespace.- Completely disallow sourcingaVim9 script, require using:import.  That  makesit difficult to use scripts for testing, or sourcing them from the  command line to try them out.Note that you CAN also use:import in legacy Vim script, see above.Compiling functions earlyFunctions are compiled when called or when:defcompileis used.  Why notcompile them early, so thatsyntax and typeerrors are reported early?Thefunctions can't be compiled right away when encountered, because there maybe forward references tofunctions defined later.  Consider definingfunctionsA,B and C, whereA calls B,B calls C, andC callsA again.  It's impossibleto reorder thefunctions to avoid forward references.An alternative would be to first scan through the file to locate items andfigure out their type, so that forward references are found, and only thenexecute thescript and compile the functions.  This means thescript has to beparsed twice, whichis slower, and some conditionsat thescript level, suchas checking ifa featureis supported, are hard to use.  An attempt was madeto see ifit works, butit turned out to be impossible to make work well.It would be possible to compile all thefunctionsat theend of the script.The drawbackis that ifa function never gets called, the overhead ofcompilingit counts anyway.  Sincestartup speedis very important, in mostcases it's better todoit later and accept thatsyntax and typeerrors areonly reported then.  Incase theseerrors should be found early, e.g. whentesting,a:defcompile commandat theend of thescript willhelp out.Why not use an existing embedded language?Vim supports interfaces to Perl, Python, Lua,Tcl anda few others.  Butthese interfaces have never become widely used, forvarious reasons.  WhenVim9 was designeda decision was made to make these interfaces lower priorityand concentrate on Vim script.Still,plugin writers may find other languages more familiar, want to useexisting libraries or seea performance benefit.  We encourageplugin authorsto write code in any language and runitas an external process, using jobsand channels.  We can try to make this easier somehow.Using an external tool also has disadvantages.  An alternativeis to convertthe tool into Vim script.  For that to be possible without too muchtranslation, and keeping the code fastat the same time, the constructs of thetool need to be supported.  SinceVim9script now includes support forclasses, objects, interfaces, and enums, thatis increasingly feasible. vim:tw=78:ts=8:noet:ft=help:norl:

Quick links:help overview ·quick reference ·user manual toc ·reference manual toc·faq


[8]ページ先頭

©2009-2026 Movatter.jp