xref: /vim-8.2.3635/README_VIM9.md (revision 89a9c159)
1![Vim Logo](https://github.com/vim/vim/blob/master/runtime/vimlogo.gif)
2
3# What is Vim9?
4
5This is an experimental side of [Vim](https://github.com/vim/vim).
6It explores ways of making Vim script faster and better.
7
8WARNING: The Vim9 script features are still under development, anything can
9break!
10
11# Why Vim9?
12
13## 1. FASTER VIM SCRIPT
14
15The third item on the poll results of 2018, after popup windows and text
16properties, is faster Vim script.  So how do we do that?
17
18I have been throwing some ideas around, and soon came to the conclusion
19that the current way functions are called and executed, with
20dictionaries for the arguments and local variables, is never going to be
21very fast.  We're lucky if we can make it twice as fast.  The overhead
22of a function call and executing every line is just too high.
23
24So what then?  We can only make something fast by having a new way of
25defining a function, with similar but different properties of the old
26way:
27* Arguments are only available by name, not through the a: dictionary or
28  the a:000 list.
29* Local variables are not available in an l: dictionary.
30* A few more things that slow us down, such as exception handling details.
31
32I Implemented a "proof of concept" and measured the time to run a simple
33for loop with an addition (Justin used this example in his presentation,
34full code is below):
35
36``` vim
37  let sum = 0
38  for i in range(1, 2999999)
39    let sum += i
40  endfor
41```
42
43| how     | time in sec |
44| --------| -------- |
45| Vim old | 5.018541 |
46| Python  | 0.369598 |
47| Lua     | 0.078817 |
48| LuaJit  | 0.004245 |
49| Vim new | 0.073595 |
50
51That looks very promising!  It's just one example, but it shows how much
52we can gain, and also that Vim script can be faster than builtin
53interfaces.
54
55LuaJit is much faster at Lua-only instructions.  In practice the script would
56not do something useless as counting but change the text.  For example,
57reindent all the lines:
58
59``` vim
60  let totallen = 0
61  for i in range(1, 100000)
62    call setline(i, '    ' .. getline(i))
63    let totallen += len(getline(i))
64  endfor
65```
66
67| how     | time in sec |
68| --------| -------- |
69| Vim old | 0.578598 |
70| Python  | 0.152040 |
71| Lua     | 0.164917 |
72| LuaJit  | 0.128400 |
73| Vim new | 0.079692 |
74
75[These times were measured on a different system by Dominique Pelle]
76
77The differences are smaller, but Vim 9 script is clearly the fastest.
78Using LuaJIT is only a little bit faster than plain Lua here, clearly the call
79back to the Vim code is costly.
80
81How does Vim9 script work?  The function is first compiled into a sequence of
82instructions.  Each instruction has one or two parameters and a stack is
83used to store intermediate results.  Local variables are also on the
84stack, space is reserved during compilation.  This is a fairly normal
85way of compilation into an intermediate format, specialized for Vim,
86e.g. each stack item is a typeval_T.  And one of the instructions is
87"execute Ex command", for commands that are not compiled.
88
89
90## 2. DEPRIORITIZE INTERFACES
91
92Attempts have been made to implement functionality with built-in script
93languages such as Python, Perl, Lua, Tcl and Ruby.  This never gained much
94foothold, for various reasons.
95
96Instead of using script language support in Vim:
97* Encourage implementing external tools in any language and communicate
98  with them.  The job and channel support already makes this possible.
99  Really any language can be used, also Java and Go, which are not
100  available built-in.
101* No priority for the built-in language interfaces.  They will have to be kept
102  for backwards compatibility, but many users won't need a Vim build with these
103  interfaces.
104* Improve the Vim script language, it is used to communicate with the external
105  tool and implements the Vim side of the interface.  Also, it can be used when
106  an external tool is undesired.
107
108Altogether this creates a clear situation: Vim with the +eval feature
109will be sufficient for most plugins, while some plugins require
110installing a tool that can be written in any language.  No confusion
111about having Vim but the plugin not working because some specific
112language is missing.  This is a good long term goal.
113
114Rationale: Why is it better to run a tool separately from Vim than using a
115built-in interface and interpreter?  Take for example something that is
116written in Python:
117* The built-in interface uses the embedded python interpreter.  This is less
118  well maintained than the python command.  Building Vim with it requires
119  installing developer packages.  If loaded dynamically there can be a version
120  mismatch.
121* When running the tool externally the standard python command can be used,
122  which is quite often available by default or can be easily installed.
123* The built-in interface has an API that is unique for Vim with Python. This is
124  an extra API to learn.
125* A .py file can be compiled into a .pyc file and execute much faster.
126* Inside Vim multi-threading can cause problems, since the Vim core is single
127  threaded.  In an external tool there are no such problems.
128* The Vim part is written in .vim files, the Python part is in .py files, this
129  is nicely separated.
130* Disadvantage: An interface needs to be made between Vim and Python.
131  JSON is available for this, and it's fairly easy to use.  But it still
132  requires implementing asynchronous communication.
133
134
135## 3. BETTER VIM SCRIPT
136
137To make Vim faster a new way of defining a function needs to be added.
138While we are doing that, since the lines in this function won't be fully
139backwards compatible anyway, we can also make Vim script easier to use.
140In other words: "less weird".  Making it work more like modern
141programming languages will help.  No surprises.
142
143A good example is how in a function the arguments are prefixed with
144"a:". No other language I know does that, so let's drop it.
145
146Taking this one step further is also dropping "s:" for script-local variables;
147everything at the script level is script-local by default.  Since this is not
148backwards compatible it requires a new script style: Vim9 script!
149
150To avoid having more variations, the syntax inside a compiled function is the
151same as in Vim9 script.  Thus you have legacy syntax and Vim9 syntax.
152
153It should be possible to convert code from other languages to Vim
154script.  We can add functionality to make this easier.  This still needs
155to be discussed, but we can consider adding type checking and a simple
156form of classes.  If you look at JavaScript for example, it has gone
157through these stages over time, adding real class support and now
158TypeScript adds type checking.  But we'll have to see how much of that
159we actually want to include in Vim script.  Ideally a conversion tool
160can take Python, JavaScript or TypeScript code and convert it to Vim
161script, with only some things that cannot be converted.
162
163Vim script won't work the same as any specific language, but we can use
164mechanisms that are commonly known, ideally with the same syntax.  One
165thing I have been thinking of is assignments without ":let".  I often
166make that mistake (after writing JavaScript especially).  I think it is
167possible, if we make local variables shadow commands.  That should be OK,
168if you shadow a command you want to use, just rename the variable.
169Using "var" and "const" to declare a variable, like in JavaScript and
170TypeScript, can work:
171
172
173``` vim
174def MyFunction(arg: number): number
175   var local = 1
176   var todo = arg
177   const ADD = 88
178   while todo > 0
179      local += ADD
180      todo -= 1
181   endwhile
182   return local
183enddef
184```
185
186The similarity with JavaScript/TypeScript can also be used for dependencies
187between files.  Vim currently uses the `:source` command, which has several
188disadvantages:
189*   In the sourced script, is not clear what it provides.  By default all
190    functions are global and can be used elsewhere.
191*   In a script that sources other scripts, it is not clear what function comes
192    from what sourced script.  Finding the implementation is a hassle.
193*   Prevention of loading the whole script twice must be manually implemented.
194
195We can use the `:import` and `:export` commands from the JavaScript standard to
196make this much better.  For example, in script "myfunction.vim" define a
197function and export it:
198
199``` vim
200vim9script  " Vim9 script syntax used here
201
202var local = 'local variable is not exported, script-local'
203
204export def MyFunction()  " exported function
205...
206
207def LocalFunction() " not exported, script-local
208...
209```
210
211And in another script import the function:
212
213``` vim
214vim9script  " Vim9 script syntax used here
215
216import MyFunction from 'myfunction.vim'
217```
218
219This looks like JavaScript/TypeScript, thus many users will understand the
220syntax.
221
222These are ideas, this will take time to design, discuss and implement.
223Eventually this will lead to Vim 9!
224
225
226## Code for sum time measurements
227
228Vim was build with -O2.
229
230``` vim
231func VimOld()
232  let sum = 0
233  for i in range(1, 2999999)
234    let sum += i
235  endfor
236  return sum
237endfunc
238
239func Python()
240  py3 << END
241sum = 0
242for i in range(1, 3000000):
243  sum += i
244END
245  return py3eval('sum')
246endfunc
247
248func Lua()
249  lua << END
250    sum = 0
251    for i = 1, 2999999 do
252      sum = sum + i
253    end
254END
255  return luaeval('sum')
256endfunc
257
258def VimNew(): number
259  var sum = 0
260  for i in range(1, 2999999)
261    sum += i
262  endfor
263  return sum
264enddef
265
266let start = reltime()
267echo VimOld()
268echo 'Vim old: ' .. reltimestr(reltime(start))
269
270let start = reltime()
271echo Python()
272echo 'Python: ' .. reltimestr(reltime(start))
273
274let start = reltime()
275echo Lua()
276echo 'Lua: ' .. reltimestr(reltime(start))
277
278let start = reltime()
279echo VimNew()
280echo 'Vim new: ' .. reltimestr(reltime(start))
281```
282
283## Code for indent time measurements
284
285``` vim
286def VimNew(): number
287  var totallen = 0
288  for i in range(1, 100000)
289    setline(i, '    ' .. getline(i))
290    totallen += len(getline(i))
291  endfor
292  return totallen
293enddef
294
295func VimOld()
296  let totallen = 0
297  for i in range(1, 100000)
298    call setline(i, '    ' .. getline(i))
299    let totallen += len(getline(i))
300  endfor
301  return totallen
302endfunc
303
304func Lua()
305  lua << END
306    b = vim.buffer()
307    totallen = 0
308    for i = 1, 100000 do
309      b[i] = "    " .. b[i]
310      totallen = totallen + string.len(b[i])
311    end
312END
313  return luaeval('totallen')
314endfunc
315
316func Python()
317  py3 << END
318cb = vim.current.buffer
319totallen = 0
320for i in range(0, 100000):
321  cb[i] = '    ' + cb[i]
322  totallen += len(cb[i])
323END
324  return py3eval('totallen')
325endfunc
326
327new
328call setline(1, range(100000))
329let start = reltime()
330echo VimOld()
331echo 'Vim old: ' .. reltimestr(reltime(start))
332bwipe!
333
334new
335call setline(1, range(100000))
336let start = reltime()
337echo Python()
338echo 'Python: ' .. reltimestr(reltime(start))
339bwipe!
340
341new
342call setline(1, range(100000))
343let start = reltime()
344echo Lua()
345echo 'Lua: ' .. reltimestr(reltime(start))
346bwipe!
347
348new
349call setline(1, range(100000))
350let start = reltime()
351echo VimNew()
352echo 'Vim new: ' .. reltimestr(reltime(start))
353bwipe!
354```
355