bmx-ng / bcc

A next-generation bcc parser for BlitzMax
zlib License
33 stars 13 forks source link

[Generics] EachIn type conversion #653

Open thareh opened 4 months ago

thareh commented 4 months ago

Good day,

Would it be possible for EachIn to be able to convert objects from generics types when possible such as the code below?

Framework BRL.Blitz
Import BRL.Collections
Import BRL.StandardIO
Import BRL.LinkedList

Type TFoo

    Field name:String = "foo"

EndType

Type TBar Extends TFoo

    Method New()
        name = "bar"
    EndMethod

EndType

Local gl:TLinkedList<TFoo> = New TLinkedList<TFoo>()
gl.AddLast(New TFoo())
gl.AddLast(New TBar())

'WORKS
For Local f:TFoo = EachIn gl
    Print f.name
Next

'DOES NOT WORK
'For Local f:TBar = EachIn list
'   Print f.name
'Next

Local l:TList = CreateList()
l.AddLast(New TFoo())
l.AddLast(New TBar())

'WORKS
For Local f:TFoo = EachIn l
    Print f.name
Next

'WORKS
For Local f:TBar = EachIn l
    Print f.name
Next

Thanks!

HurryStarfish commented 4 months ago

I think I would prefer if this wasn't allowed. The "old" EachIn using ObjectEnumerator does this by basically casting each element to the type of the variable and then skipping over Nulls, had to do it this way because it couldn't know the type of the objects in the collection, but that also causes issues. IIterable is an opportunity to improve things there. One problem is that you can't properly use EachIn on a collection that might actually have Nulls in it Some types like String get special treatment, but that makes it even more confusing. Another problem is that it makes it easier to accidentally break your code: Imagine you have a TLinkedList<TBar> and then at some point you change it to a TLinkedList<TFoo> - if you you forget to change one of the For Local f:TBar = EachIn list anywhere in your code, then that loop will now suddenly start skipping some elements, which is a pretty subtle, hard-to-find bug waiting to happen (it has happened to me before). I think it isn't not worth the risk, especially since it's not much harder to do something like

For Local f:TFoo = EachIn list
    Local b:TBar = TBar(f)
    If Not b Then Continue
    Print b.name
Next

to replicate the ObjectEnumerator behaviour explicitly.