comparison README_VIM9.md @ 19181:94eda51ba9ba v8.2.0149

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