Closed errael closed 7 months ago
For regular objects, the string() function returns the instance variables and their values. You cannot use eval() to convert the returned string back to the corresponding object. This is different from the return value of other composite objects like Lists and Dicts. I am thinking whether to align the return value of enum to be like Lists and Dicts (current behavior) or to align it with the return value for regular objects. I agree with your suggestions. I will create a PR to change the return value of string() for enums to be similar to that of regular objects and introduce a new enum_name
instance variable.
I will create a PR to change the return value of string() for enums to be similar to that of regular objects and introduce a new
enum_name
instance variable.
Do we really need enum_name
? Can't we bring back what
string(Foo.BAR)
used to produce (i.e. debug information)
so it can be aligned, if desired, to what some string(obj)
would produce? E.g.:
object of Count {count: 1, name: 'one'}
enum Count.ONE {ordinal: 0, name: 'ONE'}
And when there is need to make it terse for key lookups,
have string()
overridden (:help builtin-object-methods
)?
I did mention dictionary keys as an example, but there was
no suggestion to change textual representation for :enum
s.
And when I saw the change, it clicked, okay, its shortness
and uniqueness should fare well for key lookups so there is
no point in overriding string()
.
But can we keep the current ability of eval(string(Foo.BAR))
intact and provide similar deserialisation for objects with
eval(string(obj))
?
@errael, you need several pre-feature-merge Vim builds to
see the ‘leaky’ problem. If you source the linked example,
these lines are echoed:
(without this.name
):
16: enum Count.ONE {ordinal: 0, zero: 0}
16: enum Count.TWO {ordinal: 1, zero: 0}
(with this.name
(pay attention to v:numbermax
)):
16: enum Count.ONE {name: ONE, ordinal: 0, zero: 0}
16: enum Count.TWO {name: TWO, ordinal: 1, zero: 0}
0: 9223372036854775807
(with eval(string(Foo.BAR))
(v9.1.0219
)):
16: Count.ONE
16: Count.TWO
Do you know what would happen to keys of a dictionary for
which enumeration values, wrapped in a string, are used,
after permitted changes have been made to some of these
values?
Try it with a pre-eval(string(Foo.BAR))
build and any later
one (e.g. v9.1.0219
).
vim9script
enum Count
ONE(10), TWO(11)
var offset: number
def new(offset: number)
# Safe, enumerations are not extendable.
this.SetOffset(offset)
enddef
def SetOffset(offset: number)
this.offset = offset
enddef
def ToString(): string
try
# This eval(...) roundabout should work for enumerations
# with either "this.name" available or not (in this case
# "E121: Undefined variable: enum" is raised for
# eval('enum Count...')).
return printf("enum Count.%s {ordinal: %d, offset: %d}",
eval(string(this) .. '.name'),
this.ordinal,
this.offset)
catch /\<E121:/
endtry
return string(this)
enddef
endenum
var counts: dict<Count> = {}
for value in Count.values
counts[string(value)] = value
endfor
echo Count.ONE.ToString()
echo counts[string(Count.ONE)].offset
echo Count.TWO.ToString()
echo counts[string(Count.TWO)].offset
echo 'Mutating Count.ONE ...'
Count.ONE.SetOffset(100)
echo Count.ONE.ToString()
try
try
# BEFORE: panic at look-up.
# CURRENTLY: carry on.
echo counts[string(Count.ONE)].offset
echo Count.TWO.ToString()
echo counts[string(Count.TWO)].offset
finish
catch /\<E716:/
throw 'Remove the OLD key, then add a NEW key!'
endtry
catch /Remove the OLD key, then add a NEW key!/
try
try
echo 'Oops! Sorry about that.'
for value in Count.values
echo printf("%s: %s", string(value), value)
endfor
remove(counts, string(Count.ONE))
catch /\<E716:/
throw 'Whaaaat? Shut uuuup!'
endtry
catch /Whaaaat? Shut uuuup!/
echo 'Ugh!'
var counts_: dict<Count> = {}
for value in Count.values
counts_[string(value)] = value
endfor
counts = null_dict
counts = counts_
echo counts[string(Count.ONE)].offset
echo Count.TWO.ToString()
echo counts[string(Count.TWO)].offset
finish
endtry
endtry
Since there is an understanding that textual representation
for enumerations should be changed, it seems futile to file
a bug against its current version.
I will create a PR to change the return value of string() for enums to be similar to that of regular objects and introduce a new
enum_name
instance variable.Do we really need
enum_name
? ... And when there is need to make it terse for key lookups, havestring()
overridden (:help builtin-object-methods
)?But can we keep the current ability of
eval(string(Foo.BAR))
intact
AFAICT, it's been possible to override string()
for a while so as long as eval("Foo.BAR")
works there's no problem.
Personally, I don't like overriding string()
to create a key. string()
has a variety of uses and creating a key for an enum seems a common thing to do, so there would be interference. So I would probably do something like
vim9script
var d: dict<any>
enum Planet
earth
const as_key = 'Planet.' .. this.name
endenum
var x = Planet.earth
d[x.as_key] = 17
echo d
which is not much more work than doing
const as_key = this.enum_name .. '.' .. this.name
The main difference being that if you change the name of the enum, you don't have to remember to change the as_key
initialization. I haven't considered enough if there are other uses of the enum name, such that I'd end up creating that manually. It is a convenience that can be generally/manually provided like an interface, not required, for example
vim9script
interface ClassName
var class_name: string
endinterface
enum Planet implements ClassName
earth
const class_name = "Planet"
endenum
echo Planet.earth.class_name
Since it comes from an interface, there's an easy test to see if it's available.
In Java
you can do e.getClass().getName()
, not sure about other languages.
and provide similar deserialisation for objects with
eval(string(obj))
?
IIUC, by this you mean recovering the original object from a string. I think this would require additional support from vim builtins, like obj->as_string()
where as string returns something like "C#uniq_id" where "C" is the class name and uniq_id is it's address (can't use address if it might move after a grow). And there needs to be a string->as_object()
for going the other way.
I'm about to post something about cloning an object that seems loosely related.
you need several pre-feature-merge Vim builds to see the ‘leaky’ problem.
Thanks, I missed the word "class" in your description "class variables leak".
Do you know what would happen to keys of a dictionary for which enumeration values, wrapped in a string, are used, after permitted changes have been made to some of these values?
This is the same thing that happens if you try to use a regular class
object as a dict
index.
IIUC, by this you mean recovering the original object from a string.
But nothing beyond what is currently possible for lists,
dictionaries, and enumerations, and within the current Vim
process.
vim9script
const list: list<number> = range(4)
const list_s: string = string(list)
echo (eval(list_s) == list) (eval(list_s) is list)
const dict: dict<blob> = {a: 0z0a, b: 0z0b}
const dict_s: string = string(dict)
echo (eval(dict_s) == dict) (eval(dict_s) is dict)
enum Count
ONE, TWO
endenum
const enum_one_s: string = string(Count.ONE)
echo (eval(enum_one_s) == Count.ONE) (eval(enum_one_s) is Count.ONE)
class Unity
const one: number = 1
endclass
const class_one = Unity.new()
const class_one_s: string = string(class_one)
# E121
echo (eval(class_one_s) == class_one) (eval(class_one_s) is class_one)
IIUC, by this you mean recovering the original object from a string.
But nothing beyond what is currently possible for lists
I specifically meant recovering the "original" object. As you show, is
is false, recovering the original object is beyond what is currently possible. (with enums you always get the original object).
If you don't care about recovering the original object, only something that is ==
, that could be done by implementing a clone function on your object. For example,
vim9script
class C
var x: number
def Clone(): C
return C.new(this.x)
enddef
endclass
var o1 = C.new(17)
echo o1
var o2 = o1.Clone()
echo o2
echo o1 == o2
outputs
object of C {x: 17}
object of C {x: 17}
true
If you really want eval(string(obj))
to work, eval would have to take some arbitrary string and decide if it looks like the output of some random string(obj)
output and create an object from that; that seems unreasonable (if not impossible) and beyond what eval()
does. Still looks like a new builtin, obj_from_string(string(obj)), makes more sense; and make sure you don't override string()
.
enums to be similar to that of regular objects and introduce a new
enum_name
instance variable
If you want to implement a clone technique using eval()
, it's tricky since eval doesn't have function context. And if there's some generality, you could want the name of the class that the object is an instance of. So something like like class_name
could be handy, not sure why you'd want class_name
and enum_name
both.
class
/enum
should contain <SID>
I wasn't thinking about dict
keys in the entirety of the issue.
Note: these issues apply to both class
object and enum
value used as a dict
key.
Want the dict
key unique, meaning is
not ==
.
If there's a file /tmp/planets
with
vim9script
export enum Planet
earth
endenum
And this file is imported in
vim9script
import '/tmp/planets.vim'
enum Planet
earth
endenum
echo planets.Planet.earth
echo Planet.earth
There return from string()
may change, but the above points out the issue.
The following code is to explore how an object/value can be used as a key. This seems like it would work. And using the 'enum
s value.
vim9script
# Return something uniq per object in a class.
def GetUniqId(obj: any): string
return '17'
enddef
class MyClass
const object_id = GetUniqId(this)
const class_name = expand('<SID>') .. 'MyClass'
const as_key = this.class_name .. '@' .. this.object_id
endclass
var o = MyClass.new()
echo o.as_key
outputs something like
<SNR>36_MyClass@17
For an enum
, GetUniqId(obj)
could just be the enum name. For an object, not sure what works best. Could be a vim internal index into an array of object of given class.
The question is what class
/enum
constants used to uniquely identify an object/value should be automatically provided by vim.
Here's a hack for recovering the object from the key. It hides the original object in the dict
value.
vim9script
interface HasAsKey
var as_key: any
endinterface
def MyPut(arg_dict: dict<any>, key: HasAsKey, val: any)
arg_dict[key.as_key] = [ key, val ]
enddef
def MyGet(arg_dict: dict<any>, key: HasAsKey): any
return arg_dict[key.as_key][1]
enddef
def KeyToObj(arg_dict: dict<any>, key: HasAsKey): any
return arg_dict[key.as_key][0]
enddef
class C implements HasAsKey
static var _obj_id_count = 1
final as_key: number
def new()
this.as_key = _obj_id_count
_obj_id_count += 1
enddef
endclass
var d: dict<any>
var o1 = C.new()
var o2 = C.new()
MyPut(d, o1, 'one')
MyPut(d, o2, 'two')
echo MyGet(d, o1)
echo MyGet(d, o2)
echo KeyToObj(d, o1)
echo KeyToObj(d, o2)
echo d->keys()
echo d
outputs
one
two
object of C {as_key: 1}
object of C {as_key: 2}
['1', '2']
{'1': [object of C {as_key: 1}, 'one'], '2': [object of C {as_key: 2}, 'two']}
Note, in https://github.com/vim/vim/issues/14330#issuecomment-2028345962, there an example that suggests a dict
key that looks something like <SNR>36_MyClass@17
and a way to turn that back into an object; this needs some system support for generating the @17
unique_id and getting the object back.
There an additional issue that was missed. Since the key is simply a string, the key is not sufficient to prevent the original object from being garbage collected. So a scheme like the hack in https://github.com/vim/vim/issues/14330#issuecomment-2028442761 is required to prevent the object from being garbage collected.
As long as you take care of the keys, there are no problems.
The toy Set example below works in v9.1.0261
.
File adt.vim
:
vim9script
export class Set
final _elements: dict<number>
const _Mapper: func(number, string): any
const ToStringer: func(any): string
const FromStringer: func(string): any
static def _Mapper(F: func(string): any): func(number, string): any
return ((G: func(string): any) => (_: number, v: string): any => G(v))(F)
enddef
def new()
this._elements = {}
this._Mapper = _Mapper((s: string): any => eval(s))
this.ToStringer = (a: any): string => string(a)
this.FromStringer = (s: string): any => eval(s)
enddef
def newFromList(elements: list<any>, ToStringer: func(any): string,
FromStringer: func(string): any)
this._elements = elements
->reduce(((F: func(any): string) => (d: dict<number>, v: any) =>
extend({[F(v)]: 1}, d))(ToStringer),
{})
this._Mapper = _Mapper(FromStringer)
this.ToStringer = ToStringer
this.FromStringer = FromStringer
enddef
def _FromList(elements: list<any>): Set
return Set.newFromList(elements, this.ToStringer, this.FromStringer)
enddef
def Contains(element: any): bool
return has_key(this._elements, this.ToStringer(element))
enddef
def Elements(): list<any>
return keys(this._elements)->mapnew(this._Mapper)
enddef
def empty(): bool
return empty(this._elements)
enddef
def len(): number
return len(this._elements)
enddef
def string(): string
return string(keys(this._elements))
enddef
# {1, 2, 3} ⊇ {1, 2}.
def Superset(that: Set): bool
return (len(this._elements) >= len(that._elements)) && that._elements
->keys()
->indexof(((set: Set) => (_: number, v: string) => !set._elements
->has_key(v))(this)) < 0
enddef
# {1, 2} ⊆ {1, 2, 3}.
def Subset(that: Set): bool
return (len(this._elements) <= len(that._elements)) && this._elements
->keys()
->indexof(((set: Set) => (_: number, v: string) => !set._elements
->has_key(v))(that)) < 0
enddef
# {1, 2, 3} ∪ {2, 3, 4} = {1, 2, 3, 4}.
def Union(that: Set): Set
return this._FromList({}
->extend(that._elements)
->extend(this._elements)
->keys()
->map(this._Mapper))
enddef
# {1, 2, 3} ∩ {2, 3, 4} = {2, 3}.
def Intersection(that: Set): Set
return this._FromList(this._elements
->keys()
->filter(((set: Set) => (_: number, v: string) => set._elements
->has_key(v))(that))
->map(this._Mapper))
enddef
# {1, 2, 3} \ {2, 3, 4} = {1}.
# {2, 3, 4} \ {1, 2, 3} = {4}.
def SetDifference(that: Set): Set
return this._FromList(this._elements
->keys()
->filter(((set: Set) => (_: number, v: string) => !set._elements
->has_key(v))(that))
->map(this._Mapper))
enddef
# {1, 2, 3} △ {2, 3, 4} = {1, 4}.
def SymmetricDifference(that: Set): Set
return this.Union(that).SetDifference(this.Intersection(that))
enddef
endclass
############################################################
if !exists("g:adt_tests")
finish
endif
############################################################
const ToStr: func(number): string = (s: number) => string(s)
const FromStr: func(string): number = (s: string) => str2nr(s)
echo Set.newFromList([1, 2, 3], ToStr, FromStr)
.Subset(Set.newFromList([1, 2], ToStr, FromStr))
echo Set.newFromList([1, 2], ToStr, FromStr)
.Subset(Set.newFromList([1, 2, 3], ToStr, FromStr))
echo Set.newFromList([1, 2], ToStr, FromStr)
.Superset(Set.newFromList([1, 2, 3], ToStr, FromStr))
echo Set.newFromList([1, 2, 3], ToStr, FromStr)
.Superset(Set.newFromList([1, 2], ToStr, FromStr))
echo Set.newFromList([1, 2, 3], ToStr, FromStr)
.Union(Set.newFromList([2, 3, 4], ToStr, FromStr)).Elements()
echo Set.newFromList([2, 3, 4], ToStr, FromStr)
.Union(Set.newFromList([1, 2, 3], ToStr, FromStr)).Elements()
echo Set.newFromList([1, 2, 3], ToStr, FromStr)
.Intersection(Set.newFromList([2, 3, 4], ToStr, FromStr)).Elements()
echo Set.newFromList([2, 3, 4], ToStr, FromStr)
.Intersection(Set.newFromList([1, 2, 3], ToStr, FromStr)).Elements()
echo Set.newFromList([1, 2, 3], ToStr, FromStr)
.SetDifference(Set.newFromList([2, 3, 4], ToStr, FromStr)).Elements()
echo Set.newFromList([2, 3, 4], ToStr, FromStr)
.SetDifference(Set.newFromList([1, 2, 3], ToStr, FromStr)).Elements()
echo Set.newFromList([1, 2, 3], ToStr, FromStr)
.SymmetricDifference(Set.newFromList([2, 3, 4], ToStr, FromStr)).Elements()
echo Set.newFromList([2, 3, 4], ToStr, FromStr)
.SymmetricDifference(Set.newFromList([1, 2, 3], ToStr, FromStr)).Elements()
############################################################
const none: Set = Set.new()
echo none.len()
echo none.empty()
echo none.string()
echo string(none.Elements())
const sets: Set = Set.newFromList(
[Set.new(), Set.new(), Set.new(), Set.new()],
(o: any): string => string(o),
(s: string): any => eval(s))
echo sets.len()
echo sets.empty()
echo sets.string()
echo string(sets.Elements())
const lists: Set = Set.newFromList(
[[[[[]]]]],
(o: any): string => string(o),
(s: string): any => eval(s))
echo lists.len()
echo lists.empty()
echo lists.string()
echo string(lists.Elements())
File count.vim
:
vim9script
import './adt.vim'
interface Keyable
def AsKey(): string
endinterface
enum Count implements Keyable
ONE(10), TWO(11)
var offset: number
def new(offset: number)
# Safe, enumerations are not extendable.
this.SetOffset(offset)
enddef
def SetOffset(offset: number)
this.offset = offset
enddef
def AsKey(): string
return 'Count.' .. this.name
enddef
endenum
############################################################
const counts: adt.Set = adt.Set.newFromList(
Count.values,
(c: Count): string => c.AsKey(),
(s: string): Count => eval(s))
echo counts.len()
echo counts.empty()
echo counts.Contains(Count.ONE)
echo counts.Contains(Count.TWO)
echo counts.string()
echo string(counts.Elements())
const originals: list<Count> = deepcopy(Count.values, 1)
echo "Mutating Count.ONE ..."
Count.ONE.SetOffset(100)
for element in counts.Elements()
if index(originals, element) < 0
throw 'Illegal state'
endif
endfor
echo counts.len()
echo counts.empty()
echo counts.Contains(Count.ONE)
echo counts.Contains(Count.TWO)
echo counts.string()
echo string(counts.Elements())
Basic issue taken care of by https://github.com/vim/vim/commit/3cf121ed31f7a022e2ae6585391434d9c88e9792
As long as you take care of the keys, there are no problems.
I'd like vim
to provide a general solution. It gets pretty messy when things are imported from other files. And much more complex if you want to index by a class.
Oops, made the comment to close (Basic issue taken...), but didn't close.
If you really want
eval(string(obj))
to work,eval
would have to take some arbitrary string and decide if it looks like the output of some randomstring(obj)
output and create an object from that; that seems unreasonable (if not impossible) and beyond whateval()
does. Still looks like a new builtin,obj_from_string(string(obj))
, makes more sense; and make sure you don't overridestring()
.
Let me, for the sake of argument, bounce around a trivial
alternative. Can't we help eval()
beforehand by passing
it a string whose content it can parse? So, let's consider
a cooperation between string()
(or any method whose return
type is string
) and a defined for this task static factory
method whose return type is the type of obj
(or its any
supertype) and whose sole parameter is any of eval()
-
supported types, e.g. dictionary. In string()
, we can store
necessary for future instantiation variables as elements of
a said dictionary and convert it to a string; in our factory
method, we can take that dictionary as the passed argument
and construct a new object from its values. In other words,
we resort to Foo.FromDict(eval(string({...})))
instead of
eval(string(obj))
.
vim9script
class Pair
var a: any
var b: any
def new(this.a, this.b)
enddef
def string(): string
return string({a: this.a, b: this.b})
enddef
static def FromDict(dict: dict<any>): Pair # Or any superclass.
return Pair.new(get(dict, 'a', 0), get(dict, 'b', 0))
enddef
endclass
const pair: Pair = Pair.new("alpha", "beta")
echo pair == Pair.FromDict(eval(string(pair)))
And we can have some luck with recursive types too:
vim9script
class Zero
static const ZERO: Zero = Zero.new()
def string(): string
return string({})
enddef
endclass
class Number extends Zero
const next: Zero
const value: number
def new(this.next, this.value)
enddef
def string(): string
return string({next: this.next, value: this.value})
enddef
static def FromDict(dict: dict<any>): Zero
if empty(dict)
return Zero.ZERO
endif
var numbers: list<number> = []
insert(numbers, get(dict, 'value', 0))
var next: any = get(dict, 'next', 0)
while type(next) == type({}) && !empty(next)
insert(numbers, get(next, 'value', 0))
next = get(next, 'next', 0)
endwhile
var number: Number = Number.new(Zero.ZERO, 1)
for value in slice(numbers, 1)
number = Number.new(number, value)
endfor
return number
enddef
endclass
var _0: Zero = Zero.ZERO
echo _0 == Number.FromDict(eval(string(_0)))
var _49: Number = Number.new(Zero.ZERO, 1)
for value in range(2, 49) # E724 for 50 and greater (blob?).
_49 = Number.new(_49, value)
endfor
echo _49 == Number.FromDict(eval(string(_49)))
This example seems to be about serialization; and AFAICT, it requires knowing the type of the object in order to de-serialize. It's also a support problem, needing to maintain the to/from code when the class is changed. Seems like this manual technique requires retrofitting into any class that is part of the hierarchy; and superclass source may be owned by someone else. It doesn't seem trivial. It does seem that the output of default string()
could be parsed to get a dict
as string; there might be some kind of semi-automatic mechanism to be found.
Do you think there's a problem having/handling vim
builtin to_obj/from_obj?
Seems there are several things to consider
==
versus is
==
)is
)Most importantly, at least to me, is using an object as a dict
key. And I want to recover the original object, is
not ==
. So serialization isn't an option.
(I didn't look at the second example)
Any well-implemented support that would automate translation
to/from is more than welcome. I have shared an approach fit
for our current state of support.
Let me clarify what I meant by ‘triviality’. Implementing
it would not be trivial for string()
when not only this
is
an object but its instance variables are also objects whose
variables may also be objects, etc. But it is ‘trivial’ in
the sense that you are spared from having to roll out your
own string parser -- piggyback on dictionaries and rely on
their checks for well-formedness for free.
Furthermore, judging by the output, values produced with
default string()
implementations for dictionaries, objects,
and now enumerations look similar. They seem to contain all
data necessary to create a copy with eval()
. There can be
a problem for hypothetical eval()
support for class objects
about what non-default constructor to invoke (invariants may
be imposed to reject invalid arguments). Some cooperation
from class authors can be of help, e.g. if there is, say,
a conventionally named newDeserial()
defined, use it; if
not, try a constructor having the most parameters.
As to is
or ==
, the support for is
is out there, with
indirection. Consider using for the object keys the name(s)
of variables (possibly keyed or indexed) that hold these
objects. In the example below, I conveniently store objects
in another dictionary.
Copy adt.vim
with a toy Set implementation (augmented).
File test.vim
:
vim9script
# Generate A-Z Letter implementations and declare "g:abc_src".
source abc_gen.vim
execute 'import "' .. g:abc_src .. '" as abc'
import './adt.vim'
# Create a regular dictionary populated with object values to be used for keys
# elsewhere. Give it a short-variable name for better key lookup.
const s: dict<abc.Letter> = abc.LETTERS
->reduce((d: dict<abc.Letter>, v: abc.Letter): dict<abc.Letter> =>
extend({[v.AsString()]: v}, d),
{})
# Create another dictionary, e.g. Set, and establish indirection by referring
# with each key to the object values of the first dictionary.
const set: adt.Set = adt.Set.newFromList(abc.LETTERS,
(o: abc.Letter): string => 's.' .. o.AsString(),
(t: string): abc.Letter => eval(t))
# Recover original objects from the dictionary keys.
for object in set.Elements()
const name: string = object.AsString()
echo s[name] (s[name] is object && object is abc.ABC.Get(name))
endfor
File abc_gen.vim
:
vim9script
const head =<< END
vim9script
export interface Letter
def AsString(): string
endinterface
enum None implements Letter
NONE
def AsString(): string
return ""
enddef
endenum
var abc: dict<Letter> = {}
enum Letters
ABC
def Get(letter: string): Letter
return get(abc, letter, None.NONE)
enddef
endenum
END
const src: string = tempname() .. '.vim'
writefile(head, src)
for A in map(range(65, 65 + 25), (_, v) => nr2char(v, 1))
const a: string = tolower(A)
const body =<< trim eval END
class {A} implements Letter
const {a}: string = '{a}'
def new()
abc.{a} = this
enddef
def AsString(): string
return this.{a}
enddef
endclass
abc.{a} = {A}.new()
END
writefile(body, src, 'a')
endfor
const tail =<< END
export const ABC: Letters = Letters.ABC
export const LETTERS: list<Letter> = sort(values(abc))
export const NONE: Letter = None.NONE
END
writefile(tail, src, 'a')
g:abc_src = src
Any well-implemented support that would automate translation to/from is more than welcome.
I'm wondering if, as you've studied this issue, you've seen any issues that would create problems from doing to/from string with some vim
support?
I've never done much with Java
's serialization; I've seen that classes can intervene for some fixup and there's mechanisms for different versions of a class. I guess these issues are general and go beyond whether there's vim
support.
It's not clear to me what use cases you have in mind.
Do you think there's a problem having/handling vim builtin to_obj/from_obj?
Any well-implemented support that would automate translation to/from is more than welcome.
That is an encouragement to go for it when you really need
it. As I mentioned above, my Vim needs are usually within
its process. So if we had additional support from eval()
and {to,from}_obj
, all the better.
When I want to save/restore, say, a Vim dictionary between
program instances, I would write it to a bespoke file in Vim
syntax and source the file on demand. Nothing fancy.
Steps to reproduce
string(enum_value) no longer includes variable values.
Expected behaviour
@zzzyxwvut @yegappan
A
class
', and initially anenum
's, defaultstring()
method provides useful debug information by including instance variables in the string return. If wanted it can be overridden to something more useful to the programmer.In a recent change to
enum
classes, the defaultstring()
method now returns something of the form "Planet.earth" rather than output something like aclass
. I believe this change was made related toin comment https://github.com/vim/vim/pull/14224#issuecomment-2023939547, if
values
refers to anenum
's staticvalues
, likelist<Planet>
, then the statement doesn't make sense.string(obj)
has always included class variables.The change to
string(enum_value)
removes some useful functionality: the display of instance contents. It also diverges enum from class.There is also, in https://github.com/vim/vim/pull/14224#issuecomment-2023172587
I didn't fully understand this on first read. Looking again, it seems that the idea is to be able to use both "Planet.earth" and "Count.ONE" as keys in a dict and reconstruct the original enum value using
eval(key)
.The change to
enum
'sstring()
also handles this, but breaks expected usage ofstring()
for a class.Here's a suggestion to facilitate constructing a dict key for an enum. Add an instance variable
class_name
to an enum value. Then the developer can doA similar possibility would be adding a builtin method/var
AsKey()
/as_key
to the enum instance; thene.as_key->split('.')[0]
isPlanet
.Note that any of these changes to string() for specific purposes, like generating a dict key, seems to interfere/conflict with the idea of being able to override
string()
a.If
class_name
isn't needed/wanted, then consider something specific to enums.Version of Vim
9.1.225
Environment
ubuntu/gtk
Logs and stack traces
No response