#type stuff
1 messages · Page 1 of 1 (latest)
good decision
because i'm sure there will be more
also I forgot extend needs to have data as a first param
oh yeah i think you can just call it self and it'll understand
that's the conventional name for a this in lua
and what : does anyway
yeah I've used self a few times
well it's working, for the things I haven't defined yet
I haven't made MiningDrill yet
that's not literal tables
i mean like it shoudl be able to understand the giant literatl one-big-table like base files have
if all the smarts is there
oh do you mean like if someone does this
data:extend{{
type = "car",
-- car properties
},{
type = "item"
-- item properties
}}
yeah
that's basically the holy-grail and my understanding was we're still waiting on some LuaLS smarts to handle the narrowing once it has seen the type
it woudl also apply to various things further down the tree
it seems to be working
I'm hoping the prototype docs will be much easier to auto generate this from
i expect so
when therenas is done with it
for now, it's manual labor for me :P
on second thought
that's not a car property
yeah i think LuaLS does a slightly incorrect thing of basically pretending subclasses are union-ish
(subclass is the A:B relation, A:B,C is just A:B and A:C, since it's both that would correctly be an intersection of two subclasses, but LuaLS seems to treat it more like a subclass of unions, A:(B|C))
yeah nothing I'm trying seems to have anything to that effect
i wonder if it has a never type yet, and if the absence of that is related to this being not a thing
never is an empty set of values, which is useful surprisingly often for representing things which are impossible
such as the return value of a function that never returns
I don't think the annotations docs are completely up to date either
interesting
which is another way to spell an empty set of values
or, more usefullly, your big Prototype type & your literal table so far, when it becoems too broken to match anything
(rather than narrowing to a specific subtype)
like if you had type="flargle" in it
the loader would have a similar reaction i'm sure
but yeah, in ts (and many other languages with mature type systems) all this stuff "just works" and it's super easy to get spoiled by that
i have occasionally considered porting more of teh debugger's bits to ts2lua also, but not until their sourcemaps are functional :p
i think i would get more benefit than most though, i'm more likely to be handling an any that i found laying around somewhere than "regular" mod code would be
yeah makes sense
which is where fancy narrowing really shines
one of the things I'm trying is removing any ---@field type string from prototype definitions that way each one has a specific one like ---@field type "car"
yeah, i do tend to find better results (even in ts) with it unspecified at the higher level, because it makes "any other string" always make an invalid type
I'm still finding non-car related fields even though nothing has a generic string in the type field, hmm
yeah i think LuaLS just still doesn't do that correctly
last time i experimented heavily its narrowing was extremely limited
like how we have to rewrite .object_name so it looks like it's a global function call so we can pretend it's an extended type
lol that's jank
because only "things like type" get type narrowing in ifs
(that's waht __object_name is, and it's not real)
makes sense
yeah nothing I tried worked, LLS just isn't there for that yet I guess
so is there actually a difference between @alias thing thing.0|thing.1 and @class thing:thing.0,thing.1 besides being able to extend from the latter
the class is an intersection not a union
right right
but i think LLS doesn't have proper intersections which makes it... a mess
what does it do instead
this, apparently
considering they were exactly the same I don't think it does intersections at all
intersections are how it narrows the stated type & the value so far
or at least, how it's supposed to work
the autocomplete that it should offer you is properties taht are not yet present but would fit on objects that are in the interesection of the stated type and the type of the literal value seen so far
but it's doing a union instead
its' doing... something 🤷
i'm not sure what it actually does cleanly fits to type algebra
for my own sake, union is comparable to binary OR and intersection is comparable to binary AND
yes
it's literally that but on sets
you have to think of it as sets of values, the type itself is not actually a thing
it's just the circle you draw on the diagram to talk about the values
yeah I'm not thinking about it like a type anymore
sometimes it's easier to say that though
bonus: object types are just sugar for intersections of simpler objects with one field each. "all the objects that have this field, and also have that field"
lmao
but basically, everywhere they should have gone "oh that's intersections" LLS did something weirder instead to cover just the one case
so they have lots of things that are kinda but not quite intersections if you abuse them just right, but no &
and i assume still no never because that's part of the eliminating fields from narrowing
(unspecified fields on an object that doesn't accept extra fields are never, and you don't offer them for autocomplete once they narrow to never)
if you feel like trying to explain it
I'm good at explaining things
if alias is just a union though, wouldn't it just not work in the first place?
like in order to get the behavior we want it would have to use the second definition, right
i think my impression is that they're kind of aware of teh problem but that it's going to take a lot of work to fix it all
I'm not too familiar with the scene, all I know is that "it just works (unless it doesn't)"
like if it already worked perfectly
iirc sumneko's english isn't great which tends to frustrate me pretty quickly
the thign you want is a union of two non-overlapped types
so spellign it as subclasses is just wrong anyway
you just want to then be able to intersect that union with more things to extend it further
so the alias should work if it was done "correctly"
what you want is &
but that requires reworking all the things for peroper intersections, basically
right, because | is union\or and intersection doesn't exist yet
where does it do "intersection" again? type resolution and such
intersection-like-things are all over the place
the subclass of multiple parents should be an intersection but seems to be a subclass-of-union-ish
navigating the source code is near impossible
the autocomplete shoudl be based on the intersection of value-so-far and stated-type
i feel like there's more that i've named earlier but i don't remember now
and if you could write an intersection explicitly then you could just do alias & {more:stuff}
would this syntax exist at all or is this just wrong
---@alias baz foo&bar
that just doesnt' exist at all
but that's how you would spell an intersection if it did
I mean I know it doesn't exist now, but is this how it should be done if it did
my first real understanding of types being... this, is not helping
Your first step to learn TypeScript
I mean I knew what types were before, but as far as using them in complicated ways
An overview of the ways in which you can create more types from existing types.
i guess they consider a lot of this "everyday types" https://www.typescriptlang.org/docs/handbook/2/everyday-types.html
The language primitives.
this is a lot of things
i think the only thing lua does that ts doesn't have a reasonable spelling for already is multiple-expressions, distinct from TS's tuple types which map more directly to tuple-shaped-tables
I see
oh and operators
becasue js doesn't do operator overloads
but there's an obvious derivation of those from the way they did new i guess
the downside to typing things is that all of my previous code is not chock full of errors for things I haven't typed out yet
like type = "custom-input" is invalid because I haven't done that one yet lol
yeah that tends to happen when adding types to codebases that previously didn't have tehm
this is odd
scrolling down too far in a multi line union makes the string highlighting go away
is that a bug with LuaLS or is that something that can't really be worked around
hard to say without tryign to inspect the actual lsp messages going bakc and forth
reporting it anyway
i would lean towards luals bug though
---@meta
---@class IconSpecification:Icon.0,Icon.1,Icon.2
---@class Icon.0
---@field icons IconData.0[]
---@field icon_size SpriteSizeType
---@field icon_mipmaps? uint8
---@class Icon.1
---@field icons IconData.1[]
---@field icon_mipmaps? uint8
---@class Icon.2
---@field icon FileName
---@field icon_size uint8
---@field icon_mipmaps? uint8
---@class IconData.1:IconData.0
---@field icon_size SpriteSizeType
---@class IconData.0
---@field icon FileName
---@field tint? Color
---@field shift? vector
---@field scale double
---@field icon_mipmaps? uint8
for bilka
Hello type people, thinking about how to do the icon specification. In this example, do the .0 things have any special meaning?
i just had to generate individual names for them for sumneko so that's the index it was in the union definition
I see, so it's just part of the name?
yeah type names in sumneko can include a dot and i occasionally abuse that to make related things look related
Sweet
and i only do that if the union member didn't have its own name already some ordinary way
I feel like I'm missing some context though, how did that definition come to exist in the first place?
err
(Bilka just pointed me here and said it'd be interesting)
well i guess codegreen probably just wrote that one by hand?
but that style of thing is what i do with the unions of anonymous types in runtime json
ha no worries, the format isn't 100% set in stone yet anyways (well, one property in particular)
I'm still not sure how I'll translate IconSpecification into the new system. Mostly because I don't want to repeat all its fields everywhere it is used.
I've been writing out a lot of the typedefs manually whenever I use something that's missing
i forget, does the json have some means of listing a class/table type's parent type?
sumneko isn't smart enough to do type narrowing though so some of the complicated stuff like the iconspecification is functionally useless
if so, you can break the common stuff out into a subtype that all the variations inherit
(multiple parents is fine too)
That's what I was just thinking as well. Slight issue with it is that prototypes can only have one parent currently, so the IconSpec would need to be 'inserted' in the right spot, if it'd even be possible with a single definition of it.
that's a toughie
types are hard
Maybe proper multiple parent support would be warranted, but I don't know if there's any other uses for it, so seems a bit heavy-handed
there's a few other things it's used for
i feel like you'll end up using it more once you ahve it
not off the top of my head but I do remember typing out some stuff like that
probably
yeah i can't think specifically where but i'm pretty sure there's more
Wouldn't be too hard to implement, thinking about it.
sprite/animation layers
Just a bit tedious
(and sumneko already understands multiple inheritance, so it's easy on this end)
wait, shoudln't that top level type be a union not a class with multiple parents?
can't extend off of unions
hrm
I remember having to extend off of it for something
dark_background icon
something like that
that's annoying because the class with multiple parents is technically an intersection and maybe one day sumneko will do those properly and it'll break that
hopefully it does
lack of type narrowing is awful, please make union behave like union and not intersection, sumneko
My hope is Rai's language server project pulls through at some point, don't have much hope for LuaLS working properly ever sadly.
TIL this thread exists
LuaLS is great for the things it does work with, but it really falls flat in some of the fundamentals
welcome rai
I mentioned this yesterday, but I think I'm going to prioritize the language server over my pesticide project because while I can use the debugger through vscode just fine, LuaLS is horrible in both places and is actively annoying me every day.
He has been summoned! Guess you have a keyword notifier thing?
Yeah I have highlights for my name 😄
do you have one for rai specifically lmao
Yes
example of type narrowing not existing
While we're on it, I have something else I wanted to ask about. I have a global variable called util, and LuaLS insists on combining it with the core lualib stuff. Core util is not required anywhere. I know the easy solution is to use a different name for my variable, but I really don't want to. Is this a LuaLS issue, or is there something you could do about it jarg?
It's because fmtk has the core util in the library
What we really need is a way to have different language server sessions for data vs. control
you might be able to define a custom class and make it specifically that instead
Hm that could work. I was mainly bringing it up in case it was an oversight on fmtk's side, or at least fixable by it in some way.
could name it utils instead
that could also work, but eh I'm picky
It's not an oversight, it's intentional. Unfortunately fmtk can't tell the server to only require it some of the time, so it requires it all the time.
you could also remove util from fmtk's auto require
So it can't be tied to having require("util") somewhere in the project?
One thing I intend to do for my server is have a way to specify "only include these globals if these files are required" and specify specific root files to start from (data.lua and control.lua)
it probably is tied to seeing that soemwhere
util exists in data stage as a global though
but it may have also seen it through data files somewhere
It's not in my project, but maybe vanilla data files then?
yeah
Unfortunate, but I can live with it. Just wanted to make sure there's not something silly that would fix it.
base makes util a global in data stage, LuaLS can't separate files out, and fmtk is telling LuaLS that util is a global
Makes sense
all combined together to give you this issue
Guess the real crime is util.lua creating a global variable.
Would be fun to break all the mods by undoing that.
Globals are evil. One particularly annoying one is that one of the core lualib files defines a player global 😡
a "workspace" is meant to be one set of files that all interact, so we're the ones doing weird stuff with multiple stages of things
globals are like the only things that break when doing this
Feels like it should have the concept of a single file being the entry point, and only follow requires from there.
Yeah, that's exactly what I intend to do in mine
that will cause some trouble with globals defined in the parent file and used in the children
How so?
because the child file would have no context to see them
so it woudl think they dont' exist
ls for it would
I haven't written the server yet, we don't know what it will or won't do 🙃
well, following from what you just said
if each file only sees what it itself requiers, then it has no context for what its require-parent has set up for it
only doing this means globals defined before requires won't be seen in the required files
It could collect all globals along the way and inject them afterwards (no idea if that's work)
the solution is to not use globals
Since globals are, well, global, I intend to put them in a separate place that all files will reference for diagnostics.
thats' basically waht sumneko is doing now
so now util is global to all files again
Except it shouldn't even parse the data stage files from my control.lua file starting point right?
All files in that workspace, yes. The whole problem with LuaLS is that data stage and control stage are not separated, so it shows globals that aren't there.
being able to separate files into different scopes would be great
So getting back to IconSpecification, talked to Bilka about it, and the inheritance solution won't work for a multitude of reasons. One 'proper' solution would be to have a system where you can 'extend' a prototype with a Type, so basically you could say 'This prototype has all the properties from this type'. But that would add complication to the whole stack, just for this particular situation.
We're kinda leaning towards just duplicating the underlying docs text everywhere and being done with it. The real issue is that the IconSpec implementation is very silly, and so the docs will also be silly. If you guys can think of another solution though, we'd be happy to consider it.
Solution: make it less silly for 1.2
congrationlations, you have been selected as the winner for some free work
No not like that runs
Oh I will have lots of 1.2 stuff for you at some point :p
But also this would break literally every mod
Also the docs will be for 1.1 as well so it wouldn't even help me
it would be nice if icon_specification was a table field in the prototype instead of the prototype extending off of it
but that would break literally every existing mod
yeah exactly
I think the goal should be to not make the layering and scaling not suck too
but like, not an easy task, hence my (badly typoed aka why I shouldnt be working right now) joke
what happens if I have a class that extends from another class but has a field of the same name
does it override the base class?
i'm not suer what sumneko actually does but what it should do is the child definition wins (and must be a subset of the parent's definition)
I'm wondering if I can have a field set to false in one, then the other sets it to true and has additional properties
well, no, because now the child is not a valid member of teh parent
you can only narrow a field by redefining, not expand
I'm going to try it anyway and see if sumneko explodes
it works, surprisingly
and when it's generic it gives you both options
still no type narrowing in general, but it's better than nothing
when you make a field optional with ? is that shorthand for |nil on the end of it
yes
for fields there's no distinction between ? and |nil
but for arg/return lists there can be
since those do have a "not present"/"end of list" that is distinct from nil, especially when interacting with cfunctions
interesting
for example, script.on_event will throw if the third arg is nil-but-present when the first one isn't a single event
but for fields there's no such distinct "not-present" case
in script.on_event's definition
yeah the ? is the not-present case when it is meaningfully different
neat
which means youc an also do fun things like string|nil? in an arg list
you shouldn't but because they mean different things, you can
"just because you can..."
as it turns out this whole thing is useless in my specific scenario because all of the potential additional fields could be nil
I wish there was an easy way to define either/or properties
for all of the "this field must be present if x" in the prototype definitions
can you put a question mark on a @type
what do you mean?
this is inside of a @class literal with the actual table definition, this can be either uint or nil
---@type uint?
train_id = nil
full context
---@class PlayerData
global.players[index] = {
---@type LuaPlayer
player = player,
---@type table<string, any>
flags = {},
---@type LuaEntity[]
rename = {},
---@type uint|nil
default_train_limit = 1,
---@type boolean
restore_automatic = true,
---@type uint?
train_id = nil
}
seems to work though
i'd have expected those to be tagged with field but yeah that should be fine too 🤷
field expects a name
ah right
As I understand ? and |nil is the same
Except in arg/return lists, yeah
Oh what‘s the difference there?
? is for "can be not-present" vs "can be nil"
Which is functionally the same in Lua right? Guess it has semantic difference for the language server
it is distinct in lua also depending on how you unpack them
in particular when interacting with vararg functions, like all cfunctions
foo(nil) and foo() produce different contents of ... in foo
Ah I see
you have to try a bit harder to contrive a situation where it's obviously distinct in returns but they're teh same mechanism, so the same thing applies
but in "normal" lua with all named args and assignments stright to named vars the magic list lenght adjustments hide it all
and of course "not-present" can only be at/after the end of the list of present values
i'ts only distinguishable by explicitly checking the count though
Appreciate the explanations
the .../args/returns-tuple type is definitely the neglected one in lua, which makes it all a bit weirder to see since all interactions with it are at least a little clunky
so sumneko updated or something and now it says I have missing fields on every single prototype
uh oh
from the changelog:
NEW diagnostic: missing-fields
oh hell yeah
i think i'm gonna try and work on taht and the PR for the plugin and some other bits tomorrow then
i've been dragging on working on all the things for a while, but i'm feeling functional again maybe...
very nice
also, with this new diagnostic:
stuff like LuaGuiElement.add_param is going to have to change
currently it's just type: string and other fields
thanks
Cannot assign
{ player: LuaPlayer, [string]: LuaGuiElement }to parameter{ player: LuaPlayer, [string]: LuaGuiElement }.
the type narrowing was released a few hours ago
with the prototype doc migration progressing quite well it looks like we'll have good language server support for data stage soon
and I'm waiting on this release https://community.kde.org/Schedules/KDE_Gear_23.08_Schedule for trying it with a different text editor and that's only a month away
I‘ll change type to a union of literals. If there‘s more of these you run into, please post them into the forum thread.
@chilly mountain well, I meant that it's going to have to be a union of classes, each with their own definition for each type
Sure, that's what the LS can do, but the type param should still be a union to enable that :) It already splits out the properties depending on type, so the LS should be able to handle it somehow right?
from what I can see, no it can't
currently LuaGuiElement.add_param is one class with all of the fields of the different types of elements
so it says stuff like column_count is required, even though type is not a table element
Right, it needs to narrow it somehow. Which is the same kinda thing needed for prototypes, if I understand correctly.
here's an example of what I did for the energy source type for prototypes, there's a base class for the shared parameters, and then a bunch of individual classes for the unique parameters, and then a union all the way at the top for the actual type (there's more below for heat/void/fluid, didn't want to image wall)
And does the language server understand that as it should?
(I use that exact pattern a lot in the prototype docs, btw)
it should, if type narrowing exists now then absolutely
Well that's good then. I guess what I've been trying to say is that the runtime docs JSON for LuaGuiElement will not split it out into classes like that, but if type is the proper union of literals, FMTK will be able to do that part itself I assume.
yeah I guess with variant parameter groups
if that's the case, then I think that's something that could be done now, type string union or not
Yes, it's just that it wouldn't know about values of type that don't have additional parameters.
Since they don't figure in that list.
ahhh I see
Hence why I'm looking at turning all those type parameters into unions of literals now
Which they just should be anyways.
for clarity's sake, this is the original issue for anyone that comes across this
@clever sapphire are you auto generating the serpent.options typedef? none of the fields are marked as optional
also, I seem to have two places for runtime-api files, one in the .vscode workspace folder and one in appdata, should I delete one of those?
yeah the one in workspace is old, shouldn't exist anymore
and serpent is just a manual lib file,
cool, thanks
serpent.options should be all optional fields and not required, not sure if that's something you can update for everyone else
yeah when it cools down a bit and my AC catches up again...
wtf why only this one?!?
lmao
sure seems like i need to reverse the union for all the position types for that though lol
weird
were there mroe things for me to look at immediately or is it just functions that need their params split up in the json first?
I don't think so
I'm in the process of writing a script to detect every instance of variant parameters without a union of string literals for one of the regular parameters
but that's for therenas, not you :P
It's not that many actually
Mostly filter concepts, which are very crappy in general
And create_entity, which is a whole mess anyways
yeah that's not even based on a type
I had it working a tiny bit but I go's type system is kind of ass sometimes
@chilly mountain can we make it officially stated that union types used as return types always actually mean the first member type? i'm pretty sure that would let me adjust the generated bits to have it be teh strict type on returns and the loose type everywhere else. pretty sure it's true for all the current ones.
oh wait, hrm. maybe that's not true....
i have a note from somebody to try to do taht on all the position types but then i'm now thinkign about other places where the return type depends on what you ask for instead... 🤔
I‘d need some examples 😅 I‘m not aware of any such rule. For positions (and/or Vector-like things?) specifically, it should always return the table, not tuple, format when reading, but accept either. But even that is not quite always true if I recall correctly. It‘s on the todo list to clean up the mess for 1.2.
The mess kinda comes from read-write attributes having one type, so it needs to be the more permissive one. The proper solution is to allow for separate read and write types, which would solve your issue I think.
basically all the named or numbered tables, beucase it only pushes the named versions
because the LuaHelpers to push them are only for that version
Yes, but I‘m not sure if that‘s true 100% of the time.
it would be unless someone did the extra work to make the other kind
If they didn‘t use the helpers, yes
I have some notes on this somewhere, but it‘s late, I can look into it tomorrow.
Hm I'm not sure, my notes are unfortunately confusing. But yeah there's no formal rule that it always uses the first union member. This will be addressed in the long term by separate read and write types.
well, no, because that still won't address functions which return position, for example
... which i'm pretty sure exist, but now i'm having to look to try to fnd a specific example
but also other union concepts, like SimpleItemStack from https://lua-api.factorio.com/latest/LuaEquipmentGrid.html#LuaEquipmentGrid.take though that one is even in the "wrong" order since returns woudl always be the full ItemStackDefinition afaik (though, now that that has a name, maybe it shoudl just be used as the return type directly?)
Those are just kinda lazy documentation writing, they should be the exact type that is returned.
There is no ambiguity in the code for method returns, so there shouldn't be any in the docs.
yeah i was just thinking maybe we should just not use union types in returns at all when it's like that...
only when it actually coudl be many things (though, in many cases overload signatures woudl be better for that instead)
I can write down a note to go over these and use the more specific type.
Yeah overload is a different thing, which I also want to add at some point.
yeah i forget off hand but there's a couple places where the signatures we wrote in plain types are kinda weird because it was a hack around not having overloads
i think with this narrowing stuff there's a good chance sumenko will get good overload resolution soon too (since it's a very similar analysis)
Ah yeah that'd be nice. It's on the list to add overloads for the next version of the runtime docs.
because technically overloads are just unions of function signatures, so ti just has to narrow based on the args filled in
thank you language server