HaxeFoundation / haxe

Haxe - The Cross-Platform Toolkit
https://haxe.org
6.18k stars 655 forks source link

Unable to create a `Map<Float, Int>` #10762

Open ShaharMS opened 2 years ago

ShaharMS commented 2 years ago

Use Case

I'm implementing the hough transform in haxe, and one of the things needed to create the transform is an accumulator, this accumulator should be a 2D array, with theta:Int as its first access key, and rho:Float as the second. there are 360 thetas:

var accum:Array<Map<Float, Int>>= [for (i in 0...361) []];

and im working with that map like this:

var rho:Float;
var theta = 0.;
var thetaIndex = 0;
var x = i - Std.int(image.width / 2);
var y = j - Std.int(image.height / 2);
while (thetaIndex < 360) {
    rho = rhoMax + x * Math.cos(theta) + y * Math.sin(theta);
    rho /= 2;
    if (accum[thetaIndex][rho] == null) {
        accum[thetaIndex][rho] = 1;
    } else {
        accum[thetaIndex][rho]++;
    }
    houghSpace.setPixel(thetaIndex, Std.int(rho), houghSpace.getPixel(thetaIndex, Std.int(rho)).darken(0.02));

    theta += Math.PI / 360;
    thetaIndex++;
}

Bug

Running the code below results in a from/to error:

class Main {
    static function main() {
        new Map<Float, Int>();
    }
}1
Main.hx:4: characters 57-59 : Abstract haxe.ds.Map has no @:to function that accepts haxe.IMap<Float, Int>

just initializing a map with <Float, Int> doesnt work.

TryHaxe

https://try.haxe.org/#AC09f0D8

back2dos commented 2 years ago

There is no cross platform implementation of float maps. You have a bunch of options:

  1. if you're targeting JS you can use js.lib.Map, and other targets may have something similarly generic, or even something float specific.
  2. if your target's native int size is 64 bit and IntMap it should be possible to just cast the floats to ints
  3. depending on the possible values of rho, you can possibly convert it to an int (or e.g. its root if you need better resolution near 0) and then use that in an IntMap

You can implement your own map too, of course. I don't know how large every accumulator is. Maybe haxe.ds.BalancedTree will do the trick. Maybe you can convert the floats to strings (I expect that to be slow, but who knows). Or maybe an ordered array of (rho, counter) pairs will perform well.

ShaharMS commented 2 years ago

About closing - I think the error message should be changed. I reported this as an issue here because it seemed as a bug. Changing the error to something in the ballpark of "Initialization of a Map with key type $type is not allowed" might aid with the feeling of "its not a bug its a feature" when encountering this error

Simn commented 2 years ago

Well, this isn't really about maps in particular, it's about @:multiType. But yes, you're not the first person to be confused by this, so I suppose I'll finally relent and add a hacky error message for Map keys.

back2dos commented 2 years ago

Without special exceptions for Map, shouldn't it be possible to generally adjust @:multiType resolution failures so that for example the one above would lead to No implementation available for haxe.ds.Map<Float, Int>? It basically says the same, but it's far less intimidating for newcomers, who want to use a pretty basic data structure without having to know the ins and outs of abstracts, implicit casts and what not.

Also, perhaps supporting something like @:multiType(@:followWithAbstracts K, @:onFailure 'Key type $K is not supported for Map') to generate Key type Float is not supported for maps would be even nicer for this case, without having to deal with it in the compiler directly.