assert fails for sequence type
ePaul opened this issue · 8 comments
I'm not sure into which repository this belongs ... it is a runtime issue which doesn't happen in the JavaScript version, only in JVM (with the released 1.2, I didn't try any newer).
// value
interface V {}
// list
class Li(shared V* items) satisfies V {}
// symbol
class S(String name) satisfies V {
string => name;
}
shared void run() {
value f = Li(Li(S("#1")));
value ns = f.items[0];
value n = if (is Li ns) then ns.items else ns;
print(n);
assert (exists n);
print(className(n));
assert (is Sequential<Anything> n);
assert (exists first = n[0]);
print(first);
print(className(first));
assert (is S first);
assert (is S[] n);
//assert(is Li[] n);
print("Okay.");
}
The last assert assert(is S[] n);
fails in the JVM, but runs fine when I select "run as JavaScript application" in the IDE. (For comparison, when removing the //
in the following line, that will throw in JS.)
I'm quite sure that this assert should not fail (this is a sequence containing one S
object, as the other asserts and prints show).
Or did I understand this wrong? If so, how can I come to an [S*]
or even {S*}
from this n
?
@ePaul the type of items
is V[]
, not its subtype S[]
. But you can make Li
generic to have the more specific type:
// value
interface V {}
// list
class Li<Element>(shared Element* items)
satisfies V given Element satisfies V {}
// symbol
class S(String name) satisfies V {
string => name;
}
shared void run() {
value f = Li(Li(S("#1")));
value ns = f.items[0];
Anything n = if (is Li<out Anything> ns) then ns.items else nothing;
assert (is S[] n);
print("Okay.");
}
FYI, rather than className()
, you can use type()
to see the type with type parameters.
Interestingly, if you use
value f = Li(Li(*[S("#1")]));
in your original example, the assertion passes. The spread becomes a no-op and n
returns the more specific tuple [S]
created in run
. But I doubt this is guaranteed behavior.
Interestingly, if you use
value f = Li(Li(*[S("#1")]));
in your original example, the assertion passes. The spread becomes a
no-op and n returns the more specific tuple [S] created in run . But
I doubt this is guaranteed behavior.
Actually, I tried this, but in a different method which doesn't know anything about the type S
, so it doesn't help.
My lists are usually mixed lists (which can contain other stuff than just S
and Li
), just in this case I need one containing just symbols – therefore the type parameter won't really help, as the method constructing those Li
entries (a parser or even a user-called function) doesn't know about this.
I guess what I want is to take a V[]
and check at runtime if actually all elements are S
(and have the compiler recognize this check when successful).
It looks like this does what I want:
value sn = [*n.narrow<S>()];
assert( sn == n);
Is there some way to do this in one step, i.e. without having to compare after the filtering?
I just hacked this together:
[E&T*] assertNarrow<T, E=Anything>({E*} iterable) =>
iterable.collect((E e) {
if (is T e) {
return e;
} else {
throw AssertionError("`` e else "null" `` is not of type `` `T` ``.");
}
});
S[] snn = assertNarrow<S>(n); // passes
Li[] ln = assertNarrow<Li>(n); // fails
It works as expected.
Is something like this already there?
As far as I know that's right. Type parameters should be considered immutable, so you need to rebuild the container to change them.
I don't think anything exists for this, but I agree, it should! Maybe open an issue in ceylon.language to have something added to Iterable
?
I've run into this exact scenario once or twice, and had to really stop myself from being lazy and skipping the assertion with something like items.narrow<S>().sequence();
.
Is there some way to do this in one step, i.e. without having to compare after the filtering?
Just check the size of the two sequences, you don't need to compare all their elements.
Type parameters should be considered immutable, so you need to rebuild the container to change them.
Right.
@gavinking yes, this was just a misunderstanding (from me). Thanks for the explanations.
Cheers, thanks.