SpacemanDMM icon indicating copy to clipboard operation
SpacemanDMM copied to clipboard

Inconsistent nested list assumptions

Open Arokha opened this issue 3 years ago • 6 comments

// "Untyped" list, .Add() method
var/list/list_a = list("nested" = list())
list_a["nested"].Add("something") // Linter error

// "List-typed" list, .Add() method
var/list/list/list_b = list("nested" = list())
list_b["nested"].Add("something") // No error, declared as /list/list

// "Untyped" list, += operator
var/list/list_c = list("nested" = list())
list_c["nested"] += "something" // No error, despite lacking the /list/list

I am not sure which way you want it, but it should probably be consistent. If you are willing to doubt that the list member lacks a .Add() function, you should probably also assume it lacks support for the += operator if you are not given a type.

Or, ideally, provide a way to specify individual key-value types or at least multi-typed lists, as many lists are mixed types. Imported/Exported JSON will be mixed types in many cases, and this is also especially common when exchanging data with the various SS13 UI handlers like nano/tgui/vui.

Arokha avatar May 28 '21 17:05 Arokha

In this example:

var/list/L = list("foo" = 12)
L["foo"] += 10

There is no type annotation that even could be added to indicate that L["foo"] is a number and therefore legal to +=. Without a good alternative, dreamchecker instead does not typecheck operators such as += at all. None of the primitive types have method calls, so dreamchecker can safely typecheck those.

provide a way to specify individual key-value types or at least multi-typed lists, as many lists are mixed types

Any suggestions?

SpaceManiac avatar May 28 '21 20:05 SpaceManiac

Could just be a macro for declaring value types, like... LIST_VALUE_TYPE(list, "key", /type, initial) And it just resolves to list["key"] = initial Would that work? I do not know a ton about linting and what can be parsed from that.

EDIT: Err, I totally ignored your thing about primitives. I dunno, is it possible to have a magic keyword for that as the type?

Arokha avatar May 28 '21 21:05 Arokha

I'll also mention that the way byond does it (which is insane), according to @Leshana is that list["key"].someProc() is valid if any type has someProc() declared. So for the record byond gave up on this, I guess. XD

Arokha avatar May 28 '21 21:05 Arokha

One more demo case from @Leshana:

var/list/list/list_b = list("nested" = list())
list_b[1].Add("something")  // No linter error, declared as /list/list - runtime error
list_b["nested"].Add("something") // No linter error, declared as /list/list - no runtime error

Arokha avatar May 28 '21 21:05 Arokha

Unfortunately there's only so much dreamchecker can do within the syntactic limitations of BYOND, even with macros, and distinguishing between numeric and non-numeric keys is not something which I've arrived at a satisfactory design for.

The escape hatch is to annotate specific subexpressions with types by putting them in variables:

var/L = list("nested" = list())
var/key = L[1]
var/list/nested = L[key]

SpaceManiac avatar May 28 '21 22:05 SpaceManiac

Addressing the ambiguity between the types of the "key" and "value" of DM associative lists would be tricky to solve.

But the list type annotation syntax we have now works okay for non-associative lists at least. I think its main gap is the inability to declare lists of built in types (string, number, etc)

Leshana avatar May 28 '21 22:05 Leshana