qorelanguage / qore

Qore Programming Language
GNU General Public License v2.0
58 stars 10 forks source link

Cannot easily assign complex/hashdecl types #2045

Closed omusil24 closed 7 years ago

omusil24 commented 7 years ago

As a Qore user, I would expect better automatic type conversion...

#!/usr/bin/env qore
# -*- mode: qore; indent-tabs-mode: nil -*-

%new-style
%enable-all-warnings
%require-types
%strict-args

hashdecl HD {
    int name;
}

list<hash<HD>> params;

# 1. throws PARSE-TYPE-ERROR: lvalue for assignment operator '=' expects
# list<hash<HD>>, but right-hand side is list<hash<string, int>>
params = ({"name": 5},);

# 2. throws PARSE-EXCEPTION: cannot cast to 'list<hash<HD>>' from a list typed
# with incompatible value type 'hash<string, int>'
#params = cast<list<hash<HD>>>({"name": 5},);

# 3. throws PARSE-EXCEPTION: cannot initialize 'list<hash<HD>>' from a list
# typed with incompatible value type 'hash<string, int>'
#params = new list<hash<HD>>({"name": 5},);

# 4. throws PARSE-TYPE-ERROR: lvalue for assignment operator '=' expects
# hash<HD>, but right-hand side is hash<string, int>
# hash<HD> h = {"name": 5};

# 5. OK
# hash<HD> h = cast<hash<HD>>({"name": 5});

# 6. OK
# hash<HD> h = new hash<HD>({"name": 5});
davidnich commented 7 years ago

@omusil24 hashdecls require explicit declarations and conversions for assignments; they are not subject to automatic type folding (whereas complex hashes are, as seen in the error messages quoted above).

To make the assignment to a hashdecl, you must use the cast<> operator. The new operator also can create a hashdecl value explicitly.

This is by design - that hashdecl values can be assigned to hash lvalues, but not vice-versa without an explicit conversion (cast<>) or declaration (new).

It would be theoretically possible to implement automatic type folding to a hashdecl at parse time if all key names and value types are known at parse time, but this also would involve a "constructor"-like call in that any keys in the hashdecl that are not included in the assignment that have initializer expressions would be implicitly initialized with the assignment - to avoid that, we require explicit conversions etc as described above.

omusil24 commented 7 years ago

IMO there should be a runtime conversion there instead of parse errors/exceptions.

davidnich commented 7 years ago

@omusil24 IMO there should not because it requires an implicit constructor call

omusil24 commented 7 years ago

This makes it very user-unfriendly... Is it only because of performance reasons?

davidnich commented 7 years ago

This makes it very user-unfriendly... Is it only because of performance reasons?

no, it's just to make it clear to the programmer that it's more than a simple assignment - just like you cannot assign a hash to an object without first instantiating the object.

So when the user writes

list<hash<HD>> params = (new hash<HD>(("name": 5)),);

it takes more effort for the programmer to write out the hashdecl initialization values, but like this it's clear that there is more code being executed than just a hash assignment. In this case the hashdecl doesn't have any other keys or any member initialization expressions, but it could.

If we had support for something closer to C/C++ in Qore, it would look like this:

list<hash<HD>> params = (hash<HD>(("name": 5)),);

but this is in fact not legal; in Qore you currently need the new operator.

omusil24 commented 7 years ago

Why does it need to be clear to the programmer? He doesn't care about that. (S)he just wants to assign a hashdecl-formatted hash list to a variable of that type, which intuitively should not be a problem...

If there's more than one hash and it has more keys, this (the new-operator initialization style) would get quickly unnecessarily long and verbose.

davidnich commented 7 years ago

supporting automatic conversion to hashdecl values at runtime would leave Qore open to implicit runtime type errors, which is something that I would like to eliminate in the future (ex: the upcoming %strong-types directive). That's another strong reason for not doing it.

omusil24 commented 7 years ago

What do you mean by "implicit runtime type errors"?

davidnich commented 7 years ago

What do you mean by "implicit runtime type errors"?

I mean runtime type errors due to implicit type conversions that fail - for example - if runtime type conversion to hashdecls would be supported transparently, then:

# this would succeed
hash<HD> h0 = ("name": 5);

# but the following would fail with a runtime type error:
any val = "5";
hash<HD> h1 = ("name": val);

# as will the following:
hash h = ("name": "five");
hash<HD> h2 = h;

If we would allow the first one to succeed with an implicit runtime type conversion to a hashdecl, then the last two need to fail at parse time to eliminate the possibility of runtime type errors.

So to support the first example above, implicit runtime type conversions from a hash -> hashdecl must be supported, and to exclude the last two examples, the parse-time type checking must be deterministic (or a parse exception must be raised).

The problem is that if we allow for implicit runtime type conversions from a hash -> hashdecl, then we have more complicated runtime type matching, which for large hashdecls will have a performance impact in things like runtime variant matching (for example with call signatures with large hashdecls as parameters), which is currently very fast. I believe there are other possible runtime performance impacts.

Currently htis logic is very fast - if you didn't declare it as a hashdecl, then no attempt at implcit runtime matching is performed.

omusil24 commented 7 years ago

The problem is that if we allow for implicit runtime type conversions from a hash -> hashdecl, then we have more complicated runtime type matching, which for large hashdecls will have a performance impact in things like runtime variant matching (for example with call signatures with large hashdecls as parameters), which is currently very fast.

That's a reasonable argument. Then what about not allowing this automatic conversion for everything but just for assignments?

davidnich commented 7 years ago

because a change like that would make the language internally much more complicated and less consistent