Why Map does not work for GString in Groovy? - dictionary

With the following snippet I cannot retrieve gString from a map:
def contents = "contents"
def gString = "$contents"
def map = [(gString): true]
assert map.size() == 1 // Passes
assert gString.hashCode() == map.keySet().first().hashCode() // Passes, same hash code
assert map[gString] // Fails
How on earth is that possible?
Assertion message clearly shows that there's something seriously wrong with Groovy:
assert map[gString] // Fails
| ||
| |contents
| null
[contents:true]
It's not the same question as Why groovy does not see some values in dictionary?
First answer there suggests:
You're adding GString instances as keys in your map, then searching for them using String instances.
In this question I clearly add GString and try to retrieve GString.
Also neither Why are there different behaviors for the ways of addressing GString keys in maps? nor Groovy different results on using equals() and == on a GStringImpl have an answer for me. I do not mutate anything and I do not mix String with GString.

tl;dr: You seem to have discovered a bug in Groovy's runtime argument overloading evaluation.
Answer:
map[gString] is evaluated as map.getAt(gString) at runtime straightforwardly via Groovy's operator overloading mechanism. So far, so good, but now is where everything starts to go awry. The Java LinkedHashMap class does not have a getAt method anywhere in it's type hierarchy, so Groovy must use dynamically associated mixin methods instead (Actually that statement is sort of reversed. Groovy uses mixin methods before using the declared methods in the class hierarchy.)
So, to make a long story short, Groovy resolves map.getAt(gString) to use the category method DefaultGroovyMethods.getAt(). Easy-peasy, right? Except that this method has a large number of different argument overloads, several of which might apply, especially when you take Groovy's default argument coercion into account.
Unfortunately, instead of choosing DefaultGroovyMethods.getAt(Map<K,V>,K), which would seem to be a perfect match, Groovy chooses DefaultGroovyMethods.getAt(Object,String), which coerces the GString key argument into a String. Since the actual key is in fact a GString, the method ultimately fails to find the value.
To me the real killer is that if the argument overload resolution is performed directly from code (instead of after the operator resolution and the category method selection), then Groovy makes the right overload choice! That is to say, if you replace this expression:
map[gString]
with this expression:
DefaultGroovyMethods.getAt(map,gString)
then the argument overloading is resolved correctly, and the correct value is found and returned.

There's nothing wrong with Groovy. A GString is not a String. It is mutable and as such should never be used as a key in a map (like any other mutable object in Java).
Learn more about this in the docs: http://docs.groovy-lang.org/latest/html/documentation/index.html#_gstring_and_string_hashcodes

Related

Kotlin Bundle.putString not explicitly adding "String" but instead is "String?"

val args = Bundle()
args.putString("type", details.type)
navigator.navigate(context!!, findNavController(), Destination.TYPE, args)
I am quite confused as to why in the receiving fragment when I go to access the arguments I have passed through it is responding with...
val type: String = arguments.getString("type")
The arguments.getString is all underlined red and says "Required String Found String?" But how when I called method "putString"?!?
It is resulting in text not being rendered in the new fragment and I assume this is a nullability issue.
It's a matter of knowledge that is available in the receiving Fragment.
The Fragment is not aware of how its arguments were created (or modified) so it has to assume the "type" key you're looking for might not be in the arguments Bundle. That's why it returns a nullable (String?) result (the null value would mean absent in arguments).
Your fragment might be created in many places in your app and its arguments might have been modified in many places. We have no way of tracking that.
There are different solutions for this problem, depending on your approach in other parts of the code and how "confident" you are in creating of your Fragment.
I would usually choose a solution in which I assume setting the type is mandatory. Therefore if the type is absent - I fail fast. That would mean the Fragment was misused.
val type: String = arguments!!.getString("type")!!
The code above will crash if either:
a) arguments weren't set, or
b) String with type wasn't put in the arguments Bundle.
You are right, that is a : null ability issue.
First you should be sure if you are expecting a value, so try adding "?" or "!!", i would recommend "?", or go with the block of if {} else
To read the string safely you can use:
val type: String = arguments?.getString("type").orEmpty()
The orEmpty call at the end ensures that a valid String is returned even if either arguments or getString() returns null.
The method signature for getString() returns a nullable String. This is because at compile time, the compiler can't know if the value exists in the bundle or not. You will have the same issue when retrieving anything from any Map.
If you know for certain that the value in the bundle or map should exist at the time you call getString(), you can use the !! operator. That's what it's there for. When you know something should always be there, it is appropriate to want an exception to be thrown (in this case KNPE) if it's not there so you can easily find any programming error during testing.
isEmpty() or ?.let aren't helpful in this particular case because they would just be masking a programming error and making it harder to discover or debug.

Does Kotlin have pointers?

Does Kotlin have pointers?
If yes,
How to increment a Pointer?
How to decrement a Pointer?
How to do Pointer Comparisons?
It has references, and it doesn't support pointer arithmetic (so you can't increment or decrement).
Note that the only thing that "having pointers" allows you is the ability to create a pointer and to dereference it.
The closest thing to a "pointer comparison" is referential equality, which is performed with the === operator.
There is no pointers in Kotlin for low-level processing as C.
However, it's possible emulate pointers in high-level programming.
For low-level programming it is necessary using special system APIs to simulate arrays in memories, that exists in Windows, Linux, etc. Read about memory mapped files here and here. Java has library to read and write directly in memory.
Single types (numeric, string and boolean) are values, however, other types are references (high level pointers) in Kotlin, that one can compare, assign, etc.
If one needs increment or decrement pointers, just encapsulate the desired data package into a array
For simulate pointers to simple values it just wrap the value in a class:
data class pStr ( // Pointer to a String
var s:String=""
)
fun main() {
var st=pStr("banana")
var tt=st
tt.s = "melon"
println(st.s) // display "melon"
var s:String = "banana"
var t:String = s
t.s = "melon"
println(s.s) // display "banana"
}
I found this question while googling over some interesting code I found and thought that I would contribute my own proverbial "two cents". So Kotlin does have an operator which might be confused as a pointer, based on syntax, the spread operator. The spread operator is often used to pass an array as a vararg parameter.
For example, one might see something like the following line of code which looks suspiciously like the use of a pointer:
val process = ProcessBuilder(*args.toTypedArray()).start()
This line isn't calling the toTypedArray() method on a pointer to the args array, as you might expect if you come from a C/C++ background like me. Rather, this code is actually just calling the toTypedArray() method on the args array (as one would expect) and then passing the elements of the array as an arbitrary number of varargs arguments. Without the spread operator (i.e. *), a single argument would be passed, which would be the typed args array, itself.
That's the key difference: the spread operator enables the developer to pass the elements of the array as a list of varargs as opposed to passing a pointer to the array, itself, as a single argument.
I hope that helps.

Define a jsonable type using mypy / PEP-526

Values that can be converted to a JSON string via json.dumps are:
Scalars: Numbers and strings
Containers: Mapping and Iterable
Union[str, int, float, Mapping, Iterable]
Do you have a better suggestion?
Long story short, you have the following options:
If you have zero idea how your JSON is structured and must support arbitrary JSON blobs, you can:
Wait for mypy to support recursive types.
If you can't wait, just use object or Dict[str, object]. It ends up being nearly identical to using recursive types in practice.
If you don't want to constantly have to type-check your code, use Any or Dict[str, Any]. Doing this lets you avoid needing to sprinkle in a bunch of isinstance checks or casts at the expense of type safety.
If you know precisely what your JSON data looks like, you can:
Use a TypedDict
Use a library like Pydantic to deserialize your JSON into an object
More discussion follows below.
Case 1: You do not know how your JSON is structured
Properly typing arbitrary JSON blobs is unfortunately awkward to do with PEP 484 types. This is partly because mypy (currently) lacks recursive types: this means that the best we can do is use types similar to the one you constructed.
(We can, however, make a few refinements to your type. In particular, json.Dumps(...) actually does not accept arbitrary iterables. A generator is a subtype of Iterable, for example, but json.dumps(...) will refuse to serialize generators. You probably want to use something like Sequence instead.)
That said, having access to recursive types may not end up helping that much either: in order to use such a type, you would need to start sprinkling in isinstance checks or casts into your code. For example:
JsonType = Union[None, int, str, bool, List[JsonType], Dict[JsonType]]
def load_config() -> JsonType:
# ...snip...
config = load_config()
assert isinstance(config, dict)
name = config["name"]
assert isinstance(name, str)
So if that's the case, do we really need the full precision of recursive types? In most cases, we can just use object or Dict[str, object] instead: the code we write at runtime is going to be nearly the same in either case.
For example, if we changed the example above to use JsonType = object, we would still end up needing both asserts.
Alternatively, if you find sprinkling in assert/isinstance checks to be unnecessary for your use case, a third option is to use Any or Dict[str, Any] and have your JSON be dynamically typed.
It's obviously less precise than the options presented above, but asking mypy to not type check uses of your JSON dict and relying on runtime exceptions instead can sometimes be more ergonomic in practice.
Case 2: You know how your JSON data will be structured
If you do not need to support arbitrary JSON blobs and can assume it forms a particular shape, we have a few more options.
The first option is to use TypedDicts instead. Basically, you construct a type explicitly specifying what a particular JSON blob is expected to look like and use that instead. This is more work to do, but can let you gain more type-safety.
The main disadvantage of using TypedDicts is that it's basically the equivalent of a giant cast in the end. For example, if you do:
from typing import TypedDict
import json
class Config(TypedDict):
name: str
env: str
with open("my-config.txt") as f:
config: Config = json.load(f)
...how do we know that my-config.txt actually matches this TypedDict?
Well, we don't, not for certain.
This can be fine if you have full control over where the JSON is coming from. In this case, it might be fine to not bother validating the incoming data: just having mypy check uses of your dict is good enough.
But if having runtime validation is important to you, your options are to either implement that validation logic yourself or use a 3rd party library that can do it on your behalf, such as Pydantic:
from pydantic import BaseModel
import json
class Config(BaseModel):
name: str
env: str
with open("my-config.txt") as f:
# The constructor will raise an exception at runtime
# if the input data does not match the schema
config = Config(**json.load(f))
The main advantage of using these types of libraries is that you get full type safety. You can also use object attribute syntax instead of dict lookups (e.g. do config.name instead of config["name"]), which is arguably more ergonomic.
The main disadvantage is doing this validation does add some runtime cost, since you're now scanning over the entire JSON blob. This might end up introducing some non-trivial slowdowns to your code if your JSON happens to contain a large quantity of data.
Converting your data into an object can also sometimes be a bit inconvenient, especially if you plan on converting it back into a dict later on.
There has been a lengthy discussion (https://github.com/python/typing/issues/182) about the possibility of introducing a JSONType; however, no definitive conclusion has yet been reached.
The current suggestion is to just define JSONType = t.Union[str, int, float, bool, None, t.Dict[str, t.Any], t.List[t.Any]] or something similar in your own code.

Why recommend use ctx as the first parameter?

As the documentation said
Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx
but I found, in the typical http request handle function, a http.Request object has .Context() method can retrieve the context which http request associate with.
So why recommend to use context as the first parameter in these functions? Is that reasonable in this situation?
I know that is not an absolute rule. But I want to know why the HandlerFunc is func(ResponseWriter, *Request) instead of func(context.Context, ResponseWriter, *Request).
Apparently HandlerFunc breaks this recommendation.
As described in the documentation you quoted above, ctx should be a (very) common argument for many functions. This is similar to the way many functions return an error. The best place for a common argument/return value is either as the first, or last in a list. (Arguably, Go could have chosen to make error always be the first return value--I won't discuss that here).
Since variadic variables may only be the last in the list of function arguments, this leaves the only option for a common argument to be the first one.
I expect this is why ctx is always first.
This pattern is often seen with other variables in Go (and other languages) as well. Any time a common variable is used by a set of related functions, that common variable often comes first in the argument list (or possibly second, after ctx).
Contrary to the advice you quoted, there are libraries that store ctx in a struct, rather than passing it around as the first argument. These are usually (always?) libraries which had to be retro-fitted to use ctx, long after the library contract was set in stone (by the Go 1.x compatibility guarantee).
Generally, you should follow the advice to pass ctx as the first argument, for any new work.

Why I cannot get exactly the same GString as was put to map in Groovy?

With the following snippet I cannot retrieve gString from a map:
def contents = "contents"
def gString = "$contents"
def map = [(gString): true]
assert map.size() == 1 // Passes
assert gString.hashCode() == map.keySet().first().hashCode() // Passes, same hash code
assert gString.is(map.keySet().first()) // Passes, exactly the same object
assert map[gString] // Fails
How is that possible?
What's interesting here is that map.get(map.keySet()[0]) works fine while map.get[map.keySet()[0]] does not.
Assertion message clearly shows that there's something wrong:
assert map[gString] // Fails
| ||
| |contents
| null
[contents:true]
It's not the same question as Why groovy does not see some values in dictionary?
First answer there suggests:
You're adding GString instances as keys in your map, then searching for them using String instances.
In this question I clearly add GString and try to retrieve GString.
Also neither Why are there different behaviors for the ways of addressing GString keys in maps? nor Groovy different results on using equals() and == on a GStringImpl have an answer for me. I do not mutate anything and I do not mix String with GString. Groovy documentation is not helpful as well.
tl;dr: You seem to have discovered a bug in Groovy's runtime argument overloading evaluation.
Answer:
map[gString] is evaluated as map.getAt(gString) at runtime straightforwardly via Groovy's operator overloading mechanism. So far, so good, but now is where everything starts to go awry. The Java LinkedHashMap class does not have a getAt method anywhere in it's type hierarchy, so Groovy must use dynamically associated mixin methods instead (Actually that statement is sort of reversed. Groovy uses mixin methods before using the declared methods in the class hierarchy.)
So, to make a long story short, Groovy resolves map.getAt(gString) to use the category method DefaultGroovyMethods.getAt(). Easy-peasy, right? Except that this method has a large number of different argument overloads, several of which might apply, especially when you take Groovy's default argument coercion into account.
Unfortunately, instead of choosing DefaultGroovyMethods.getAt(Map<K,V>,K), which would seem to be a perfect match, Groovy chooses DefaultGroovyMethods.getAt(Object,String), which coerces the GString key argument into a String. Since the actual key is in fact a GString, the method ultimately fails to find the value.
To me the real killer is that if the argument overload resolution is performed directly from code (instead of after the operator resolution and the category method selection), then Groovy makes the right overload choice! That is to say, if you replace this expression:
map[gString]
with this expression:
DefaultGroovyMethods.getAt(map,gString)
then the argument overloading is resolved correctly, and the correct value is found and returned.

Resources