vim9script and null

168 views
Skip to first unread message

Ernie Rael

unread,
Mar 4, 2022, 9:05:41 PM3/4/22
to vim...@googlegroups.com
I saw some recent msgs about comparing anything to null, and that it
would be allowed. I guess I got the wrong impression. Using vim:
"Included patches: 1-4507"

In my first vim9 script, I did (the real code conditionally return null)

def Get(): string
    return null
enddef

And this doesn't compile because "Type mismatch; expected string but got
special".

Allowing null seems like an improvement.

In my case, I can do "def Get(): any", but then there's a runtime checking.

Would allowing it to be null incur some additional runtime check? The
following fails at runtime, so that can't be it entirely:

var xxx = 'foo'
xxx = 'bar'
xxx = null

I guess how to handle a "null string" might be part of the issue. In my
case, when the value is null, it only gets used in a comparison.

-ernie

PS I could file this for consideration, if it would be useful.

Bram Moolenaar

unread,
Mar 5, 2022, 5:48:28 AM3/5/22
to vim...@googlegroups.com, Ernie Rael

Ernie Rael wrote:

> I saw some recent msgs about comparing anything to null, and that it
> would be allowed. I guess I got the wrong impression. Using vim:
> "Included patches: 1-4507"
>
> In my first vim9 script, I did (the real code conditionally return null)
>
> def Get(): string
>     return null
> enddef
>
> And this doesn't compile because "Type mismatch; expected string but got
> special".
>
> Allowing null seems like an improvement.

I am still looking into how we should use null. In many places null is
handled the same as an empty string. In this example you might as well
return an empty string, since it will be the same for most purposes.

There are also complications, such as when assigning null to a variable,
when getting that value it looks like the variable wasn't initialized,
so it will be initialzed then and when it is a list, get an empty list
instead of null. That might be confusing.

Also, a null list cannot store a type, because the type is kept on the
list. Not sure if this matters, since this only matters for declared
variables, which, as just mentioned, are initialized to an empty
list/dict/etc., and then the type is added.

> In my case, I can do "def Get(): any", but then there's a runtime checking.

That is not a good solution, any reason you can't return an empty
string?

> Would allowing it to be null incur some additional runtime check? The
> following fails at runtime, so that can't be it entirely:
>
> var xxx = 'foo'
> xxx = 'bar'
> xxx = null
>
> I guess how to handle a "null string" might be part of the issue. In my
> case, when the value is null, it only gets used in a comparison.

That's what was implemented so far, comparing with null.


--
"Hit any key to continue" is a lie.

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

Ernie Rael

unread,
Mar 5, 2022, 4:29:06 PM3/5/22
to Bram Moolenaar, vim...@googlegroups.com
On 3/5/22 2:48 AM, Bram Moolenaar wrote:
> Ernie Rael wrote:
>
>> I saw some recent msgs about comparing anything to null, and that it
>> would be allowed. I guess I got the wrong impression. Using vim:
>> "Included patches: 1-4507"
>>
>> In my first vim9 script, I did (the real code conditionally return null)
>>
>> def Get(): string
>>     return null
>> enddef
>>
>> And this doesn't compile because "Type mismatch; expected string but got
>> special".
>>
>> Allowing null seems like an improvement.
> I am still looking into how we should use null. In many places null is
> handled the same as an empty string. In this example you might as well
> return an empty string, since it will be the same for most purposes.
>
> There are also complications, such as when assigning null to a variable,
> when getting that value it looks like the variable wasn't initialized,
> so it will be initialzed then and when it is a list, get an empty list
> instead of null. That might be confusing.

It seems the idea of reading a null variable, except when comparing, gives
an empty item is OK, but since I haven't tried coding with it...

I said empty item is OK except when comparing. But, assuming a null string,
what about when compariing to something other than null? For example

if sval == ''

is that true or false when sval is null? I'd say true, which feels
consistent with the idea of null representing an empty item and

if sval == null

is true if and only if when sval is null. The comparison explicitly to null
would be the only way to detect null vs empty. This would seem to avoid the
java-NPE type issues. There seems to be little opportunity for confusion.

>
> Also, a null list cannot store a type, because the type is kept on the
> list. Not sure if this matters, since this only matters for declared
> variables, which, as just mentioned, are initialized to an empty
> list/dict/etc., and then the type is added.
This means that assigning null to a structured item looses the previous
type of the contents. I'm not familiar enough with vim9 to have an
opinion or feeling as to what issues might arise, but...

BTW, it is disconcerting that "string sval" or "list<string> l_sval" does
not work :-) But var works pretty well and it doesn't seem to be and issue.

At the end of this msg is a more complete example of where/how I want the
null assignment

>
>> In my case, I can do "def Get(): any", but then there's a runtime checking.
> That is not a good solution, any reason you can't return an empty
> string?
>
>> Would allowing it to be null incur some additional runtime check? The
>> following fails at runtime, so that can't be it entirely:
>>
>> var xxx = 'foo'
>> xxx = 'bar'
>> xxx = null
>>
>> I guess how to handle a "null string" might be part of the issue. In my
>> case, when the value is null, it only gets used in a comparison.
> That's what was implemented so far, comparing with null.
>
>

vim9script

# Here's a version of what I'm after.
# Note: sNull is introduced to bypass vim9 null type checking
#       since can't assign null to string

var sNull = '-=XYZZY=-'
var table = { foo: 'bar', baz: sNull }

# if not a global setting, do the default behavior
# global setting of '' or 'None' returns null
# null return means do not process the return
def Get(key: string): string
    var val = g:->get('my_settings_' .. key, null) ### NOTE: vim likes this
    if val == 'None' || val == ''
        return sNull                ### .vimrc say don't do anything
    endif
    if val == null                  ### not specified, do the default
        val =  table->get(key, sNull)
        val = val != sNull ? '-' .. val : sNull
    endif
    return val
enddef

# if Get(xxx) != sNull

echo '1' Get('foo')
echo '2' Get('baz')
echo '3' Get('bad-key')


Bram Moolenaar

unread,
Mar 6, 2022, 8:49:46 AM3/6/22
to vim...@googlegroups.com, Ernie Rael, Bram Moolenaar

Ernie Rael wrote:

> >> I saw some recent msgs about comparing anything to null, and that it
> >> would be allowed. I guess I got the wrong impression. Using vim:
> >> "Included patches: 1-4507"
> >>
> >> In my first vim9 script, I did (the real code conditionally return null)
> >>
> >> def Get(): string
> >> =C2=A0=C2=A0=C2=A0 return null
> >> enddef
> >>
> >> And this doesn't compile because "Type mismatch; expected string but got
> >> special".
> >>
> >> Allowing null seems like an improvement.
> > I am still looking into how we should use null. In many places null is
> > handled the same as an empty string. In this example you might as well
> > return an empty string, since it will be the same for most purposes.
> >
> > There are also complications, such as when assigning null to a variable,
> > when getting that value it looks like the variable wasn't initialized,
> > so it will be initialzed then and when it is a list, get an empty list
> > instead of null. That might be confusing.
>
> It seems the idea of reading a null variable, except when comparing, gives
> an empty item is OK, but since I haven't tried coding with it...
>
> I said empty item is OK except when comparing. But, assuming a null string,
> what about when compariing to something other than null? For example
>
> if sval == ''
>
> is that true or false when sval is null? I'd say true, which feels
> consistent with the idea of null representing an empty item and
>
> if sval == null
>
> is true if and only if when sval is null. The comparison explicitly to null
> would be the only way to detect null vs empty. This would seem to avoid the
> java-NPE type issues. There seems to be little opportunity for confusion.

If we consider an empty string equivalent to null, then both if these
should return true:
if sval == ''
if sval == null

If you want to check the difference, you would use "is":
if sval is null

This only works for null, since an empty string will be a different
instance every time.

On the other hand, there is a difference between null and a string with
value null. The latter has the string type, which matters in several
places. I'm not sure using null itself is a good idea. Your example at
the end of the message shows that you actually want a string with null
value, which in this case is to be considered different from an empty
string.

We actually have these values, such as with test_null_string(). These
were thought only to be useful for testing, but perhaps we should alow
for using them generally?

Another request was to be able to clear a script-local variable.
Assigning null would be one solution:
var F: func
var work: job
...
F = null
work = null

Currently this fails. And there is no way to clear "F" or "work". If
we make this work by actually assigning null to "F" and "work", then the
type is lost. What we probably want is something like:
F = test_null_function()
work = test_null_job()

That clears the variable without dropping the type. Same for your case
of wanting to have a string with null value:
d = {a: 'text', b: test_null_string()}

That way the type of the dictionary is clearly dict<string>. If we
would use:
d = {a: 'text', b: null}

Then the type is dict<any>, since we don't know what type the null
stands for, could be anything.

> > Also, a null list cannot store a type, because the type is kept on the
> > list. Not sure if this matters, since this only matters for declared
> > variables, which, as just mentioned, are initialized to an empty
> > list/dict/etc., and then the type is added.

I just tried this, and it seems to work OK. The type is also stored
with the variable, and it looks like that is used when needed.

> This means that assigning null to a structured item looses the previous
> type of the contents. I'm not familiar enough with vim9 to have an
> opinion or feeling as to what issues might arise, but...

Yes, that is the problem we need to tackle. Considering the above, how
about introducing different null types:

null_string
null_dict
null_list
null_function
null_partial
null_channel
null_job

That will allow for clearing a script-local variable, while keeping the
type. And it allows for using a null value in a dict or list while
keeping the type.

Another example, consider preparing a list with jobs, but the jobs have
not been created yet:
var jobs = {read: null_job, write: null_job, check: null_job}
...
jobs.read = job_start(...)
if writing
jobs.write = job_start(...)
endif
jobs.check = job_start(...)
...
if jobs.write != null_job
# write
endif

This looks better than just using "null" and handling the type
difference in some way.

Another problem not mentioned yet: When using a default value for a
function argument, and the type is channel, there was no value that
could be used. null_channel would be useful there too.

> BTW, it is disconcerting that "string sval" or "list<string> l_sval" does
> not work :-) But var works pretty well and it doesn't seem to be and issue.

There are different ways to do declarations in various languages. Using
"var" is similar to JavaScript and happens to fit well with Vim
commands.


--
If Microsoft would build a car...
... You'd have to press the "Start" button to turn the engine off.

Ernie Rael

unread,
Mar 6, 2022, 4:02:17 PM3/6/22
to Bram Moolenaar, vim...@googlegroups.com
On 3/6/22 5:49 AM, Bram Moolenaar wrote:
>
> Yes, that is the problem we need to tackle. Considering the above, how
> about introducing different null types:
>
> null_string
> null_dict
> null_list
> null_function
> null_partial
> null_channel
> null_job
>
> That will allow for clearing a script-local variable, while keeping the
> type. And it allows for using a null value in a dict or list while
> keeping the type.

This scheme certainly satisfies my requirements. One question, there's

        if jobs.write != null_job

but what about

        var sval = ''
        if sval == null_string

is this true or false? Or is string a special case and you need

        if sval is null_string

Requiring 'is' is confusing

-ernie

Yegappan Lakshmanan

unread,
Mar 6, 2022, 4:18:51 PM3/6/22
to vim_dev, Ernie Rael, Bram Moolenaar
Hi,

On Sun, Mar 6, 2022 at 5:49 AM Bram Moolenaar <Br...@moolenaar.net> wrote:
>
>
> Another example, consider preparing a list with jobs, but the jobs have
> not been created yet:
>

The Vim9 LSP plugin uses this construct for the lsp server jobs.
I have run into the problem that you described below. It will
definitely help to simplify the code if the null_xxx values are
added.

Regards,
Yegappan

Bram Moolenaar

unread,
Mar 6, 2022, 5:44:25 PM3/6/22
to vim...@googlegroups.com, Ernie Rael, Bram Moolenaar

Ernie Rael wrote:

> On 3/6/22 5:49 AM, Bram Moolenaar wrote:
> >
> > Yes, that is the problem we need to tackle. Considering the above, how
> > about introducing different null types:
> >
> > null_string
> > null_dict
> > null_list
> > null_function
> > null_partial
> > null_channel
> > null_job
> >
> > That will allow for clearing a script-local variable, while keeping the
> > type. And it allows for using a null value in a dict or list while
> > keeping the type.
>
> This scheme certainly satisfies my requirements. One question, there's
>
>         if jobs.write != null_job
>
> but what about
>
>         var sval = ''
>         if sval == null_string
>
> is this true or false? Or is string a special case and you need
>
>         if sval is null_string
>
> Requiring 'is' is confusing

In many places an empty string and a null string are considered equal.
This is mainly for practical reasons.

When do you need to know whether a string is really null or empty?
That should be rare. I would think in those cases you likely used
"null_string". In the help for that we can add a remark to use "is" to
check whether the value really is null.

The same is the case for list and dict, empty and null are often
considered the same value.

For job and channel, which were part of the request to support for
clearing a variable, there is no such thing as "empty". Perhaps a job
that failed to be created could be equal to null_job? It looks like it
does, job_status() returns "fail" then.

--
A bad peace is better than a good war. - Yiddish Proverb
Reply all
Reply to author
Forward
0 new messages