Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add static type hints for dictionary members #56

Closed
ghost opened this issue Sep 10, 2019 · 89 comments · Fixed by godotengine/godot#78656
Closed

Add static type hints for dictionary members #56

ghost opened this issue Sep 10, 2019 · 89 comments · Fixed by godotengine/godot#78656
Milestone

Comments

@ghost
Copy link

ghost commented Sep 10, 2019

Describe the project you are working on:
Cutscene-heavy RPG

Describe how this feature / enhancement will help your project:
I want to associate some object with some identifier to make it easy to reference them from the AnimationPlayer (in this case it's some Dialogue class with data for who is talking and what they're saying.) I would like to access this data using the format read_dialogue(id: String), where the id is the key in a dictionary that leads to the data. This causes my extended AnimationPlayer to pause the animation so it can autotype some text and wait for player input before moving on with the animation.

However, there's currently no way to define a dictionary to have any particular kinds of keys and values, so if, for example, I wanted to add 50 different lines of text to read in this animation, I'd have to set the key's type and the value's type manually every time, which is tedious.

image

Furthermore: without dictionary hints, it's not possible to add a hint to a particular kind of Object, since the list of types you can pick from is limited to built-ins and various primitive types. Contrast this to arrays, where you can use hints to specify just about any type not present in the drop-down. So, you're not going to be able to easily create resources in-place, resulting in the OmniResource issue (except worse since it's every object ever.)

Show a mock up screenshots/video or a flow diagram explaining how your proposal will work:

export(Dictionary, String, Resource) var hinted_dictionary

64645509-ab31a700-d3da-11e9-8d57-f0ca866362b2

If this enhancement will not be used often, can it be worked around with a few lines of script?:
I think just about everyone will be very pleased with this addition; however, in the meantime, it's not too bothersome to avoid the issue. Instead I'll give my class an id: String property and loop through an array. I assume this isn't as efficient as using dictionaries, but I likely won't notice a real performance hitch. Nonetheless, it's a shame exported dictionaries can't be more useful.

godotengine/godot#25157

@RichardEllicott
Copy link

this would be really nice, yes at the moment for example i really wanted to add a dictionary such that i can add other types not on the list, for example:

<String, AudioStream> for a keyed list of sounds i can load from the inspector

we have it already with lists

@Calinou Calinou changed the title Dictionary Hints Add dictionary type hints for exported variables Oct 30, 2020
@SoDaRa
Copy link

SoDaRa commented Jan 8, 2021

This would be a very nice feature. I'm doing a VN and would like to replicate the functionality of SpriteFrames in Animated Sprite to help pack all the images for a character into a scene to make it easier to compartmentalize. But using SpriteFrames causes all the images in it to be loaded, even if they aren't used. Which is understandable, but if we packed over 100 images into a character, suddenly it's going to eat up a lot of video memory and likely won't play nice with mobile. All I really need is the path to each image so I can load them when they're needed. So a Dictionary would be a perfect way to replicate the SpriteFrames resource when calling on the sprite with parameters of which sprite resources need to be loaded. But setting up the key and value type for hundreds of images is a chore. So this would help a lot.

@cgbeutler
Copy link

cgbeutler commented May 14, 2021

While I don't know/care what the short-hand export syntax would look like
It seems like typing exports for dictionaries and arrays may be better as a more "deep" style. I think this has been mentioned elsewhere, but I couldn't find it. The 'property info' dictionaries are used all over, like for function parameters and return values. The 'property info' syntax could be used here as well to hold sub-types. This may actually reduce complexity in some places.

In the context of _get_property_list it may look something like:

func _get_property_list() -> Array:
  return [
    { name = "my_dict", type = TYPE_DICTIONARY,
      hint_types = [
        { type = TYPE_STRING }, #key property
        { type = TYPE_INT, hint = PROPERTY_HINT_RANGE, hint_string = "0,10" }, #value property
      ]
    }
  ]

The above somewhat mimics the "Generics" list in some languages, which may also play well when hooking c# in.
I imagine the name field could be added for subtypes, if that seems like its easier. It is usually ignored for function return types and a few other spots.

The editor itself could then do a more recursive-ish rendering of inspectors when it comes to Dictionaries or Arrays. There would be no real depth-limit to the underlying representation.
The short-hand export may still have a depth limit, but there would be a long-hand way to get around it, if needed.

As array stands in 3.3, export(Array, Array, float) var arr := [] will give you the property info {class_name:, hint:24, hint_string:19:3:, name:arr, type:19, usage:8199}. The hint string '19:3:' appears to just be a concat of type enums, which kinda cuts out any options or custom resources.

@Calinou Calinou changed the title Add dictionary type hints for exported variables Add static type hints for dictionary members Jun 14, 2021
@Calinou Calinou added this to the 4.x milestone Feb 8, 2022
@chexmo
Copy link

chexmo commented Mar 25, 2022

I came here from godotengine/godot#25157 since someone said that that issue continues here.
I want to know if there are any news related to exporting dictionaries...
As you can see in the image below, in godot 3.4.3 it still doesn't support typed dicts
image

@Calinou
Copy link
Member

Calinou commented Mar 25, 2022

I want to know if there are any news related to exporting dictionaries...

This feature isn't planned for 4.0, as we don't intend to add new features to GDScript to ensure it can stabilize for 4.0.

@sairam4123
Copy link

sairam4123 commented May 6, 2022

Is this feature planned for at least 4.x? I'd love to see this feature in 4.1 or 4.2.
Some kind of approval from code developers could be good to hear!

@YuriSizov
Copy link
Contributor

YuriSizov commented May 6, 2022

Is this feature planned for at least 4.x? I'd love to see this feature in 4.1 or 4.2.

Some kind of approval from code developers could be good to hear!

I think it's fair to say that it is desired by people who use static typing inside and outside of the core team. But we'll get to it when we get to it. Unless @vnen wants to pinky promise?.. 🙃

@Frontrider
Copy link

Frontrider commented Jun 16, 2022

Godot 4 has typed dictionaries (I think) and arrays, so I doubt that import hints are required anymore.

@Calinou
Copy link
Member

Calinou commented Jun 16, 2022

Godot 4 has typed dictionaries and arrays, so I doubt that import hints are required anymore.

Godot 4.0 only has typed arrays, not typed dictionaries.

@bitbrain
Copy link

It is not explicitly stated here but a syntax like this would be great (and consistent with the Array syntax too)

@export var dictionary : Dictionary[String, Node] = {}

@aalhitennf
Copy link

aalhitennf commented Oct 23, 2022

It is not explicitly stated here but a syntax like this would be great (and consistent with the Array syntax too)

@export var dictionary : Dictionary[String, Node] = {}

But what happens with nested dictionarys? This syntax works only if dictionary is used like hashmap, allowing only one type as value. Lets say you parse json (that can have various types, nested dictionaries of various types etc) to dictionary, that would require typescript-like interfacing or something, otherwise it seems a bit pointless and it would make more sense to introduce completely new type (hashmap, map or whatever), instead of allowing typing for just this one use case.

@bitbrain
Copy link

But what happens with nested dictionaries?

If you have to nest dictionaries, perhaps introducing new types might be a good alternative. This makes things way more readable and easier to test. Compare this (pseudo-code)

var dictionary: Dictionary[Dictionary[Dictionary[String, int], int], int] = {}

to something like this instead:

class MyKeyType:
   # content here

var dictionary: Dictionary[MyKeyType, int] = {}

@Frontrider
Copy link

Frontrider commented Oct 24, 2022

Yeah, nested dictionaries are a whole another beast, but I'd expect them to work the same. I guess we see a reason why some languages opt for "<>" to mark typing, especially if you use square brackets for arrays. My eyes do start looking for the array in that.

@aalhitennf
Copy link

aalhitennf commented Oct 24, 2022

class MyKeyType:
   # content here

var dictionary: Dictionary[MyKeyType, int] = {}

This works but adds little overhead in cases where the data is updated fairly often since you need to turn those dictionaries into class objects (or update them) every time.

@PoisonousGame
Copy link

@export var dictionary : Dictionary{String:Node} = {}

@export var dictionary : Dictionary<TKey,TValue> = {}

@export var dictionary : Dictionary[String, Node] = {}

@bitbrain
Copy link

@export var dictionary : Dictionary{String:Node} = {}

@export var dictionary : Dictionary<TKey,TValue> = {}

@PoisonousGame those would not be consistent with the Array syntax:

@export var array : Array[String] = []

unless we say that we should change that syntax too (breaking change -> would break dozens of already migrated projects that use Godot 4)

@Frontrider
Copy link

@export var dictionary : Dictionary[String, Node] = {}

IF 4 gets it then it should be this.

@Rubrickus
Copy link

I hope this proposal gets some love. I'm fairly new to Godot, and adding entries to dictionaries in the editor is pretty painful, due to the giant types menu (which you have to select from twice for every new entry).

As a (possibly much simpler) partial workaround at the editor level, what about a modification of the "Add Key/Value Pair" button (or maybe a contextual menu?) for "Add Key/Value Pair With Same Types as Previous Entry? (I'll admit I don't know how to convey that succinctly in text!). I think this would greatly improve the most common painful use case without needing any change to the parser etc. (I realize this is a distinct proposal; I'll probably file it as such next.)

@StatisMike
Copy link

StatisMike commented Jan 27, 2024

Reading through the discussion there I came to a conclusion, that the static-type Dictionary isn't that good of a concept, and introducing such could be suboptimal and confusing.

Current dictionaries are powerful because every value can be of different type, so the key-value type declaration wouldn't really work in that context. The next questions asked would be to introduce mixed types declaration (or declarations like key Y should have X type - which is implemented in @squk plugin already).

IMO the best solution would be to introduce new type, like HashMap (key needs to be hashable, which many types already implements), where the type of both key and value would have fixed types. That way we could have properly typed key-value collections.

Additionally, from what I've seen in the source code there is actually something like HashMap implemented, but used only internally. For public usage some additional work would be needed for sure, but the foundations are already there.

It also have additional benefit for GdExtensions - many of statically typed languages support something similar to a HashMap. Having the separate type that have (at least to some extend) guarantee for type-safety on Godot's side may streamline the work on the GdExtension developers side.

TLDR:

  • separate key-value type could be better suited than introducing Dictionary[K, V] declarations
  • Dictionaries to some extend use the different types on different key, so it could be confusing to mix these types

@BlackDragonBE
Copy link

@StatisMike Hard disagree. Static dictionaries should work the same as Array, which means you can use it to store Variants until you decide to make it static, at which point it only accepts the type you defined.
A static Array looks like this:

var my_array : Array[Node]

Here's what a static Dictionary should look like IMHO to keep the syntax similar:

var my_dictionary : Dictionary[String, Node]

That doesn't seem confusing to me at all. It keeps the existing flexibility, but gives you the choice to make it static when required. Adding another type just to add static typing seems silly to me.

@StatisMike
Copy link

@BlackDragonBE I agree with the take on static-typed array - it doesn't make sense to store X at the n position, and Y at the n+1 position (or would be just a very questionable design), so the Array[T] declaration was a great choice.

I don't think that Dictionary is as unequivocal, and it can have its use to expect T1 at K1, T2 at K2 end etc (as in linked plugin). On the other hand there seems to have been some work already done on C# representation of Dictionary to make it more like a HashMap than Named List. I don't agree wholeheartedly with that decision, but it makes sense to venture in that direction with these steps already done.

@SquidgySapphic
Copy link

SquidgySapphic commented Jan 27, 2024

@StatisMike I don't see why you believe it would be any more confusing than statically-typed arrays. You can still use varying-type Arrays and static-type Arrays in the engine with that style of syntax, so why does expanding that feature to a Dictionary cause confusion?

I'm possibly off the mark, but introducing HashMap as a GDScript-level structure only serves to add an extra definition of something that doesn't really address the issue completely - if you were suggesting something that would serve as a fix for the syntax of Dictionary[varying_type, static_type] in lieu of GDScript not currently providing a way to explicitly type-hint something as Variant then I could get behind that, but that's not what I'm assuming you mean.

@mrpedrobraga
Copy link

@StatisMike (& @SquidgySapphic)

Also, well, strong typed languages using hash maps have them have strongly typed, too.
Using them like this, they are efficient storage/retrieval structures, where you want to store data of some type.

use hashbrown::HashMap;

fn main() {
    // this dictionary is my baby boy and i love him
    let mut m = HashMap::<&str, i32>::new();
    
    m.insert("A", 3);
    // look at him gooo
    dbg!(m.get("A"));
}

Allowing Dictionaries to be typed in this way can provide both more type hints for a smoother/safer programming experience, but might drastically improve the performance of Dictionary in necessary cases by removing the need for all the indirection and type checks.

Variant Type can perhaps be achieved by Dictionary[ String, ... ] or Dictionary[ ..., Transform2D ]. Perhaps not, but whatever it'd look like isn't a conversation about whether typed dictionaries should be a thing or not.

@Frontrider
Copy link

@StatisMike as it was stated previously, dictionaries are meant to be the exact same thing as a hashmap by design, but the explicit typing is missing.

The one "derailment" is that people use this hashmap as a struct.

@Kiaazad
Copy link

Kiaazad commented Feb 9, 2024

I'm adding a simple loot table to my game and an array with nested dictionaries can make my life easier. Something like:
@export_dict_array[ { "item":Item, "weight":int } ]
This way every item I add comes with the two item and weight keys already set, and there's no chance for typos.
I imagine the simple dictionary version would be:
@export_dict{ "key_1":int, "key_2":int }

@cy1mat1auto
Copy link

I don't see why something like [Export] Godot.Collections.Dictionary<string, Texture2D> shouldn't work in editor; it's already supported in code, where it's least useful.

I want to create a custom resource with character names as keys and a picture of the character as values. Without static type keys and values, I have scroll through every possible type of value to reach the bottom of the list to the "load" option in order to load a single texture2D for a single key:value pair. That's not sustainable for more than one or two textures, so dictionaries are essentially unusable for anything other than storing a few int/float/string pairs constructed in code.

One of the key purposes of dictionaries is to store MANY things.

@TrickyFatCat
Copy link

@StatisMike Hard disagree. Static dictionaries should work the same as Array, which means you can use it to store Variants until you decide to make it static, at which point it only accepts the type you defined. A static Array looks like this:

var my_array : Array[Node]

Here's what a static Dictionary should look like IMHO to keep the syntax similar:

var my_dictionary : Dictionary[String, Node]

That doesn't seem confusing to me at all. It keeps the existing flexibility, but gives you the choice to make it static when required. Adding another type just to add static typing seems silly to me.

Sounds great. It will give a choice and keep consistency.

@duchainer
Copy link

duchainer commented Mar 6, 2024

Variant Type can perhaps be achieved by Dictionary[ String, ... ] or Dictionary[ ..., Transform2D ]. Perhaps not, but whatever it'd look like isn't a conversation about whether typed dictionaries should be a thing or not.

@mrpedrobraga, I think that we might not even need special case syntax here:
Dictionary[ String, Variant ] or Dictionary[ Variant, Transform2D ]
should look and work fine. as the default type of an "untyped" Dictionary would be
Dictionary[ Variant, Variant ]
But I might be bike-shedding here 😅

@chexmo
Copy link

chexmo commented Mar 6, 2024

I kinda undestand the logic behind doing sth like Dictionary[Variant, Node2D]
BUT I think sth like Dictionary[ _, Node2D] fits better with the GDScript style guide.

@chrisl8
Copy link

chrisl8 commented Mar 6, 2024

Would structs #7329 supersede this proposal?

Structs and typed dictionaries have different purposes:

* Structs define _varied_ types in a single data structure.

* Typed dictionaries define a _single_ type for keys and values.

Structs are good when you want to define something such as a method configuration or complex return value. On the other hand, typed dictionaries are more suited for large sets of data with little to no nesting (e.g. a dictionary that maps item names to colors).

I believe we should support both.

If/when this and/or structs are added, something very similar to the above text should be added to the Godot documentation. It greatly helps to clarify when to use each.

@produno
Copy link

produno commented Mar 6, 2024

I kinda undestand the logic behind doing sth like Dictionary[Variant, Node2D]

BUT I think sth like Dictionary[ _, Node2D] fits better with the GDScript style guide.

We already use Variant in several other places, like parameters in functions for example. So i don't see why it would be any different here.

@Frontrider
Copy link

BUT I think sth like Dictionary[ _, Node2D] fits better with the GDScript style guide.

I don't think that is a good idea, because underscore may be used to mark an "unused" argument in some languages. Even at the best day this would not be an unused argument.

@Spartan322
Copy link

Spartan322 commented Mar 6, 2024

Array[Variant] already does that, its already part of GDScript intrinsically, otherwise one is making the argument for Array[_] which makes no sense, plus this is type-specific, so you need an "any" type, which Variant already serves in GDScript like Object does in C# and Java.

@chexmo
Copy link

chexmo commented Mar 7, 2024

Array[Variant] already does that, its already part of GDScript intrinsically, otherwise one is making the argument for Array[_] which makes no sense, plus this is type-specific, so you need an "any" type, which Variant already serves in GDScript like Object does in C# and Java.

In the case you need Array[_], you are better doing just Array in GDScript (without square brackets), which is cleaner to the sight IMHO and you still don't have the specific type you need, tho.

@chexmo
Copy link

chexmo commented Mar 7, 2024

It took me a while, but i've read all the thread to know what are we discussing at this point (After 2 years of participating in this thread).

What I feel it's clear at this point is that Dictionary is a key-value system, and not a struct-like one. If you need a struct, a Resource is enough for most cases.

Then, we need a disambiguation here...

  • One thing is the feature to bring full typed dictionaries support to Godot (if it already hasn't yet).
  • Other thing is to add typed dictionaries to GDScript language.

In the case of Godot.... if the feature is plugged in the editor both the inspector and all languages (GDScript, C#, and other through connectors) could benefit from it. Maybe this is enough for most of the non-GDScript users here, and it's a reasonable feature to refine and deliver.

In the case of GDScript, the problem with dictionaries and adding typing-hints to be type safe in GDScript is that you may need only keys or only values to be typed... There is no problem with Dictionary[String, Resource] because types are clear. The problem seems to appear when one of the types are not intended to be strictly typed.
If the verdict of the developers decides that Dictionary[Variant, Resource] is the best way... I'll believe in them and use that as it is in the future, which is in fact what we actually need, the feature.

However, as long as it seems to be open for discussion what syntax we should use.... My grain of salt is "please don't use Variant for variants"... Why? There's a funny quote of Uncle Bob that I like a lot that says "People keep coding Fortran in many other modern languages"... GDScript is the possibility of offering a modern spiced language that adapts specifically to the Godot gamedev needs and still look so good people says "I want to write code with this too" when they see me code. I believe a refreshing syntax will emerge. Maybe _ isn't the best wildcard... How about using ? as a wildcard for variants? In some languages like Java you can use it to tell the compiler you don't want to specify a type in certain scenarios. Maybe we don't want wildcards at all... isn't any a keyboard in GDScript? And if at final you decide to keep with Variant, go ahead. Users who are here need the feature.

@Spartan322
Copy link

Spartan322 commented Mar 7, 2024

@chexmo You're looking for #7329, this is only for type-strong dictionaries. Also _ is not a type in any language and GDScript it absolutely would make no sense, Variant is the type that already does what you want _ to do inherently, that's not what placeholder variable names are for, even in C# and JS _ has never referred to a type, its a variable name that means "I don't care about this variable's value".

@speakk
Copy link

speakk commented Mar 9, 2024

It took me a while, but i've read all the thread to know what are we discussing at this point (After 2 years of participating in this thread).

What I feel it's clear at this point is that Dictionary is a key-value system, and not a struct-like one. If you need a struct, a Resource is enough for most cases.

Then, we need a disambiguation here...

* One thing is the feature to bring full typed dictionaries support to Godot (if it already hasn't yet).

* Other thing is to add typed dictionaries to GDScript language.

In the case of Godot.... if the feature is plugged in the editor both the inspector and all languages (GDScript, C#, and other through connectors) could benefit from it. Maybe this is enough for most of the non-GDScript users here, and it's a reasonable feature to refine and deliver.

In the case of GDScript, the problem with dictionaries and adding typing-hints to be type safe in GDScript is that you may need only keys or only values to be typed... There is no problem with Dictionary[String, Resource] because types are clear. The problem seems to appear when one of the types are not intended to be strictly typed. If the verdict of the developers decides that Dictionary[Variant, Resource] is the best way... I'll believe in them and use that as it is in the future, which is in fact what we actually need, the feature.

However, as long as it seems to be open for discussion what syntax we should use.... My grain of salt is "please don't use Variant for variants"... Why? There's a funny quote of Uncle Bob that I like a lot that says "People keep coding Fortran in many other modern languages"... GDScript is the possibility of offering a modern spiced language that adapts specifically to the Godot gamedev needs and still look so good people says "I want to write code with this too" when they see me code. I believe a refreshing syntax will emerge. Maybe _ isn't the best wildcard... How about using ? as a wildcard for variants? In some languages like Java you can use it to tell the compiler you don't want to specify a type in certain scenarios. Maybe we don't want wildcards at all... isn't any a keyboard in GDScript? And if at final you decide to keep with Variant, go ahead. Users who are here need the feature.

Imo this is not the thread to be discussing renaming variants to ? or _.
This is about typed dictionaries. Whether variants get renamed at some point is not relevant to this topic.

(I'm saying this because this would be a huge improvement to gdscript, and imo we don't need any more derailment in this thread)

@monkeez
Copy link

monkeez commented Mar 20, 2024

Can We get this for 4.2 or is it too late? I would like to use this for mapping scenes to string names so I can get around the cylindrical scene references bug

as for what it would look like... GDScript

var map : Dictionary[string, PackedScene]

C#

var map = new Dictionary<string, PackedScene>();

Would this proposal solve this issue about not being able to use a PackedScene as an exported Dictionary value? Or is that something else entirely?

I was surprised to find that I couldn't add PackedScenes as exported Dictionary values already, as I was trying to make a simple database to help with loading scenes based on certain keys.

@shinspiegel
Copy link

Can We get this for 4.2 or is it too late? I would like to use this for mapping scenes to string names so I can get around the cylindrical scene references bug

as for what it would look like... GDScript

var map : Dictionary[string, PackedScene]

C#

var map = new Dictionary<string, PackedScene>();

After looking into the typed array I imagined that dictionaries would use the same pattern and this was already implemented.

If I may ask, how can we help on this feature?

@AThousandShips
Copy link
Member

AThousandShips commented May 16, 2024

At this time there isn't really anything that needs doing from any contributor, we're in feature freeze for 4.3 so it won't be merged right now, but hopefully it can be reviewed early in the 4.4 release cycle, it's a large feature so it's been hard for reviewers to find the time to focus on it properly

Though testing the feature is always welcome, you'd need to check out the PR and rebase it and compile it yourself

Edit: Though I recall there has been some discussion on what might need to be figured out before merging that might still be important, like the way generics works in GDScript and how to resolve that, things like casting typed arrays etc., which is currently an issue, and there's been discussion on whether to fix those first and then merge the typed dicts or fix that after merging

@btarg

This comment was marked as off-topic.

@Mantissa-23
Copy link

@AThousandShips with Dev1 released for Godot 4.4, I noticed this feature was missing- I gotta say it's probably my most anticipated feature right now just for the inspector ergonomics/correctness.

Would you happen to know the status of this?

@AThousandShips
Copy link
Member

I'm not in any loops around this feature so I can't really respond sorry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Implemented
Development

Successfully merging a pull request may close this issue.