More vim9 script developements

115 views
Skip to first unread message

Bram Moolenaar

unread,
Jan 12, 2020, 2:28:49 PM1/12/20
to vim...@googlegroups.com

Quite often a script keeps state at the script level. That means using
script-local items. Until now these were kept in the "s:" dictionary.
As with function-local items, using a dictionary is slow. For Vim9
script we want to make it fast.

As before, when fixing performance we might as well fix some
Vim-specific "weirdness". No other language uses "s:" variables. And
the associated use of <SID> and <SNR>. Still, Vim needs them to avoid a
script putting everything in the global namespace.

Looking around in popular languages, it turns out that JavaScript had a
very similar problem: Everything went into the global namespace. Some
half-solutions with creating a new namespace were used, but eventually
in ES6 the import/export mechanism was introduced to do this properly.
This is also used by TypeScript.

It looks like using import and export statements works very well for Vim
script, and very similar to how it works for JavaScript. Basically, you
can write a Vim script just like before, except that all items at the
script level are script-local. Nothing goes into the global namespace.
Without any export, the script can be sourced, does its work, and leaves
no trace behind. Except when it explicitly uses "g:" to put something
in the global namespace or defines a user command.

Example script one.vim:

namespace

let cache = {}

export def GetItem(key: string): string
...
let result = get(cache, key, '')
if result == ''
result = ... lookup item
cache[key] = result
endif
return result
enddef

Example script two.vim:

import GetItem from './one.vim'

let item = GetItem(key)


Notice that "cache" remains local to one.vim. And in GetItem() it can
be located at compile time, thus during execution it can be accessed
fast, without computing a has of the key and doing a dictionary lookup.

In script two.vim only one specific item from one.vim is obtained.
"GetItem" is not defined in the global namespace.


When a plugin grows bigger it is convenient to split it up in multiple
files. Then each file will want to export some things, and keep other
things private. Also, it is possible to make a library, that exports a
well defined set of items, without polluting the global scope.

I'm currently experimenting with this:

- Use a "namespace" command at the top of the script to specify that
items at the script level are script-local.
- Use "import" commands to get functionality from other scripts.
- Use "export" commands to make functionality available to other
scripts.

For the performance part, a restriction will be that script-local items
are defined once and not deleted. That makes it possible to find them
by index, which is much faster than a dictionary lookup.


--
BEDEVERE: Stand by for attack!!
[CUT TO enormous army forming up. Trebuchets, rows of PIKEMEN, siege
towers, pennants flying, shouts of "Stand by for attack!" Traditional
army build-up shots. The shouts echo across the ranks of the army.
We see various groups reacting, and stirring themselves in readiness.]
ARTHUR: Who are they?
BEDEVERE: Oh, just some friends!
"Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///

Ben Fritz

unread,
Jan 13, 2020, 8:52:47 AM1/13/20
to vim_dev
> a restriction will be that script-local items
are defined once and not deleted. That makes it possible to find them
> by index, which is much faster than a dictionary lookup.

Does this mean "unlet" will no longer work for local variables if namespaces are used? How about exists(), does this add special cases?

Bram Moolenaar

unread,
Jan 13, 2020, 12:42:59 PM1/13/20
to vim...@googlegroups.com, Ben Fritz
Correct, to be able to find the declared variables by fixed position,
they cannot be removed. Otherwise a function defined in the script that
uses the variable would break. Or have to check at runtime that it
still exists, which slows it down.

In normal programming languages one can never remove a declared
variable, thus it can't be much of a disadvantage. It's more getting in
line with normal programmers expectations. That is for variables
declared in the script namespace. Global variables can still be deleted
(and thus are slower to access), no plans to change that.

Perhaps there could be an optimization that when a variable is only used
at the script level and not anywhere in a function it could be dropped.
Don't even need to use :unlet, it would be automatic. However, the gain
is small and making it work correctly is complicated. In case the
variable actually uses a lot of memory, making it empty would be the
solution.

BTW: after implementing a few pieces of this I think the "namespace"
statement may be misleading. The effect is much more than creating a
namespace. Something like "vim9script" would be more appropriate.

And yes, this denies my original statement that the script level would
not be using Vim9 script syntax, thinking that it is not needed because
it is only executed once. There are performance reasons to also use
Vim9 script syntax at the script level. Maybe it's going a bit too far,
let's see how it works out. At least I think we should not have a third
syntax for the script level, somewhere in between the legacy and the
Vim9 script one. Better to limit to two syntax styles. Thus for script
level it's all or nothing.

--
Why is "abbreviation" such a long word?
Reply all
Reply to author
Forward
0 new messages