Hello again! (It's been a while since I was last here...)
I've been bitten by a mistake I'm making involving implicit casting. In my program, some operation is resulting in different types for a list, so == does not work.
My question is, how can I explicitly cast a list to its specialised type?
Str[] val := (Str[])getList()
Type.of(val) // => sys::Obj?[]
The issue is getList not returning a Str[] value, in spite of the type signature. The only (Type-based) way I found to make the program print "true" is changing getList:
str := Str[,]
Edit:
After writing the above, I finally fixed my original program's Type problem, which was caused by using map with an it-block:
This follows through with closures. List.map() creates and returns a new List, but it doesn't know what data will be in the new List unless we tell the compiler.
Here we tell the compiler that the closure will always return an Int, so the compiler can safely create and return a List of Int[].
lst10 := [1,2,3].map { x*x } // <-- Obj[]
But here, the closure could return any object, so the only safe option is for the compiler to create and return a List of Obj[].
Any casting (of Lists) done by any subsequent code help us, the developer, infer what should be in the List, but does not change the backing type of what was actually created.
lst11 := [1, 3, 5] // <-- a list of Int[]
lst12 := (Obj[]) lst11 // <-- still a list of Int[]
I hope this helps sure up your understanding.
pclFri 26 Jan
Thanks @SlimerDude for the explanation. It confirms what I understood, and your statement:
"The actual Type is what the List object is instantiated as, and this cannot be changed."
implies the list equals method should never be used unless you have control over list construction.
Consider this:
class Main
{
static Void main(Str[] args)
{
w := Wrap("HeLlO".chars.map { it.lower } ) // this creates sys::Obj?[]
w.check
}
}
class Wrap
{
Int[] word
new make (Int[] word) { this.word = word }
Void check()
{
echo(word[0..1] == ['h','e'])
}
}
If there's no way to cast/force the backing type of "word" in Wrap's constructor to type Int[], then == cannot be used in "check".
What is recommended practice here, if you're writing a library for others to use? Do you write your own "equalContents" method?
There seem to be a few methods which rely on == which would similarly need rewriting, e.g. remove:
fansh> x := [[1], [4], [9]]
[[1], [4], [9]]
fansh> x.remove([4])
[4]
fansh> x
[[1], [9]]
fansh> x = [[1], [4], [9]]
[[1], [4], [9]]
fansh> x.remove([2].map {it*it})
null
fansh> x
[[1], [4], [9]]
SlimerDudeFri 26 Jan
Hi @pcl,
... implies the list equals method should never be used unless you have control over list construction.
... There seem to be a few methods which rely on == which would similarly need rewriting,
It really comes down your requirements, what it is you're trying to do, and what arguments your library API will accept.
The List.equals() method does what it does (checks that two Lists are exactly equal), if you need different behaviour then yes, you'll need to write your contentsAreEqual() method. And if you accept lists of lists, then yes, your contentsAreEqual() will need to be recursive.
If you're developing for SkySpark (as most Fantom developers are!) then you may wish to consider haystack::Etc.listEq.
pcl Thu 25 Jan
Hello again! (It's been a while since I was last here...)
I've been bitten by a mistake I'm making involving implicit casting. In my program, some operation is resulting in different types for a list, so == does not work.
My question is, how can I explicitly cast a list to its specialised type?
The issue is illustrated by:
which prints "false".
What can I do to "val" to make it a true Str[]?
An explicit cast does not work:
The issue is getList not returning a Str[] value, in spite of the type signature. The only (Type-based) way I found to make the program print "true" is changing getList:
Edit:
After writing the above, I finally fixed my original program's Type problem, which was caused by using map with an it-block:
SlimerDude Thu 25 Jan
Hi
@pcl
,Everything you mentioned is correct.
To surmise, when creating a List there is the actual list Type and there is the implied runtime list Type.
The actual Type is what the List object is instantiated as, and this cannot be changed.
lst1
has to be of TypeObj[]
because we don't know what will be added to it, so, um, what else could it be!?The compiler is intelligent enough to work out what data the List is instantiated with, and types the List appropriately.
This can also be set explicitly.
This follows through with closures.
List.map()
creates and returns a new List, but it doesn't know what data will be in the new List unless we tell the compiler.Here we tell the compiler that the closure will always return an
Int
, so the compiler can safely create and return a List ofInt[]
.But here, the closure could return any object, so the only safe option is for the compiler to create and return a List of
Obj[]
.Any casting (of Lists) done by any subsequent code help us, the developer, infer what should be in the List, but does not change the backing type of what was actually created.
I hope this helps sure up your understanding.
pcl Fri 26 Jan
Thanks @SlimerDude for the explanation. It confirms what I understood, and your statement:
"The actual Type is what the List object is instantiated as, and this cannot be changed."
implies the list equals method should never be used unless you have control over list construction.
Consider this:
If there's no way to cast/force the backing type of "word" in Wrap's constructor to type Int[], then == cannot be used in "check".
What is recommended practice here, if you're writing a library for others to use? Do you write your own "equalContents" method?
There seem to be a few methods which rely on == which would similarly need rewriting, e.g. remove:
SlimerDude Fri 26 Jan
Hi
@pcl
,It really comes down your requirements, what it is you're trying to do, and what arguments your library API will accept.
The
List.equals()
method does what it does (checks that two Lists are exactly equal), if you need different behaviour then yes, you'll need to write yourcontentsAreEqual()
method. And if you accept lists of lists, then yes, yourcontentsAreEqual()
will need to be recursive.If you're developing for SkySpark (as most Fantom developers are!) then you may wish to consider haystack::Etc.listEq.