bmx-ng / bcc

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

Operator overloading: slices [begin .. end] #601

Open GWRon opened 1 year ago

GWRon commented 1 year ago

When I saw that @scaremonger used the "myTIntMap[intKey]"-array-approach I asked myself if it would be useable to have the "array slicing" being kind of useable too.

newIntMap = oldIntMap[beginKey .. endKey]
'or maybe only (which would be "more similar" to arrays
newIntMap = oldIntMap[beginIndex .. endIndex]

This way it would be really nifty to extract eg a subset from an "sparse ID map" (1,5,6,10,11,12 -> oldMap[5 .. 11] would extract 5,6,10,11) - with array like behaviour one needed to find the "index/position" of a given key first.

Of course you might be able to do similar stuff in a T(xyz)Map on your own with a custom "extractMap(condition...)" function/method.

So just see this as a suggestion / brain fart / idea. Maybe it has benefits I am not even thinking about - or culprits not solveable to easily :)

... or is "slicing" overrideable already?

HurryStarfish commented 1 year ago

... or is "slicing" overrideable already?

It isn't.

I considered this idea before, and I think that rather than adding .. as an overloadable operator, a better solution would to add a new "range" type to the standard library, which would represent expressions of the form x..y.

In other words, BRL.Blitz could get a new struct SRange like this:

Struct SRange
    Field ReadOnly startIndex:Int
    Field ReadOnly endIndex:Int

    Method New(startIndex:Int, endIndex:Int)
        Self.startIndex = startIndex
        Self.endIndex = endIndex
    End Method
End Struct

and the .. operator would just become syntactic sugar to create an instance of this struct:

myNewCollection = myOldCollection[5..11]
' is now the same as:
myNewCollection = myOldCollection[New SRange(5, 11)]

Since we already have overloadable index operators, with this, any collection type could support slicing by simply adding a Method Operator []:TMyCollection(range:SRange). No special syntax for "slice overloads" would be needed. Another upside is that you could store ranges in variables, pass and return them from functions etc., like any other value. The built-in array types would be updated to make this work for arrays as well:

Local a:Int[] = [1, 2, 3, 4, 5]
Local r:SRange = 1..3
Local b:Int[] = a[r]

The built-in array types would be updated to make code like this work with arrays and collections alike.

Issues:

GWRon commented 1 year ago
Local a:Int[] = [1, 2, 3, 4, 5]
Local r:SRange = 1..3
Local b:Int[] = a[r]

"1..3" -- think this sooner or later might clash with ".." line concatenation if you allowed Local r:SRange = 1.. ("open end").

Regarding your proposed solutions: I consider the first option

Field ReadOnly hasStartIndex:Int and Field ReadOnly hasEndIndex:Int

as the "most simple" one - and also something we Blitzmax users are "used to" (we are used to primitives having a default "null" value and no real "None/Null")

HurryStarfish commented 1 year ago

think this sooner or later might clash with ".." line concatenation

I can't really think of any realistic situation where this would result in code that still compiles (and does the wrong thing), so you'd at least notice it from getting a compile error... but yes, it might. In that case, you would have to write Local r:SRange = (1..) instead.

I consider the first option as the "most simple" one

It would be the simplest to implement right now, that's true. Personally, my favourite would be the third option (SOptional). Unfortunately, that would require such a feature first. But on the other hand, it would be a really useful feature to have in general.

Oh yeah, yet another option I forgot mention is the way it's done in .NET (this was implemented after I first considered this for BlitzMax). Their solution:

This means they don't use nullable/optional, and they have the additional feature of "counting from the end": a[...^3] in C# means "give me a slice of everything except the last 3 elements". But in exchange, it has the disadvantage that it isn't possible to distinguish [..x] from [0..x] (so collections always have to start at 0), and it only supports Int32.