www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Problem with using std.math: abs and std.complex: abs at the same time

reply berni <someone something.org> writes:
The following code doesn't compile:

import std.stdio;

void main()
{
    import std.complex: abs, complex;
    import std.math: abs;

    auto a = complex(1.0,1.0);
    auto b = 1.0;

    writeln(abs(a));
    writeln(abs(b));
}
The error message depends on the order of the two import statements. Seems like the second import is actually ignored. I hoped for a mechanism similar to overloading, which makes the compiler decide, which "abs" to use, depending on the type of the operand. Is there a way to do this? (As the code appears inside a template, something like std.math.abs() with static import doesn't work out well.)
Sep 18 2019
next sibling parent Andrea Fontana <nospam example.com> writes:
On Wednesday, 18 September 2019 at 12:03:28 UTC, berni wrote:
 The following code doesn't compile:

import std.stdio;

void main()
{
    import std.complex: abs, complex;
    import std.math: abs;

    auto a = complex(1.0,1.0);
    auto b = 1.0;

    writeln(abs(a));
    writeln(abs(b));
}
What about https://run.dlang.io/is/PGasQD ?
Sep 18 2019
prev sibling next sibling parent Daniel Kozak <kozzi11 gmail.com> writes:
On Wed, Sep 18, 2019 at 2:05 PM berni via Digitalmars-d-learn
<digitalmars-d-learn puremagic.com> wrote:
 The following code doesn't compile:

import std.stdio;

void main()
{
    import std.complex: abs, complex;
    import std.math: abs;

    auto a = complex(1.0,1.0);
    auto b = 1.0;

    writeln(abs(a));
    writeln(abs(b));
}
The error message depends on the order of the two import statements. Seems like the second import is actually ignored. I hoped for a mechanism similar to overloading, which makes the compiler decide, which "abs" to use, depending on the type of the operand. Is there a way to do this? (As the code appears inside a template, something like std.math.abs() with static import doesn't work out well.)
import std.stdio; void main() { import std.complex: abs, complex; import std.math: mabs = abs; auto a = complex(1.0,1.0); auto b = 1.0; writeln(abs(a)); writeln(mabs(b)); }
Sep 18 2019
prev sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 18 September 2019 at 12:03:28 UTC, berni wrote:
 The following code doesn't compile:

import std.stdio;

void main()
{
    import std.complex: abs, complex;
    import std.math: abs;

    auto a = complex(1.0,1.0);
    auto b = 1.0;

    writeln(abs(a));
    writeln(abs(b));
}
The error message depends on the order of the two import statements. Seems like the second import is actually ignored.
import std.complex: abs, complex; Is treated by the compiler as if you'd written something somewhat like this: static import std.complex; alias abs = std.complex.abs; alias complex = std.complex.complex; When you add import std.math: abs; It's treated like: static import std.math; alias abs = std.math.abs; In other words, we have two aliases with the same name inside a function scope, and the compiler can't do that (try doing it explicitly, as in the 'treated like' sections above - you'll get a compilation error). This is related to why you can't have overloaded nested functions: unittest { void fun() {} void fun(int i) {} // declaration fun is already defined } How to resolve this, though? The simplest solution is to not use selective imports: import std.math; import std.complex; writeln(abs(complex(1.0,1.0))); writeln(abs(1.0)); If you absolutely can't contaminate the scope with all the unused symbols in std.math and std.complex, you *can* do this: import std.stdio; unittest { import std.complex : complex; static import std.math; alias abs = MergeOverloads!(std.complex.abs, std.math.abs); auto a = complex(1.0,1.0); auto b = 1.0; writeln(abs(a)); writeln(abs(b)); } template MergeOverloads(T...) { alias MergeOverloads = T[0]; static if (T.length > 1) { alias MergeOverloads = MergeOverloads!(T[1..$]); } } I would however label that a horrible hack. FWIW, I've filed this issue: https://issues.dlang.org/show_bug.cgi?id=20226 -- Simen
Sep 18 2019
next sibling parent reply berni <someone something.org> writes:
On Wednesday, 18 September 2019 at 12:37:28 UTC, Simen Kjærås 
wrote:
 How to resolve this, though? The simplest solution is to not 
 use selective imports:

     import std.math;
     import std.complex;

     writeln(abs(complex(1.0,1.0)));
     writeln(abs(1.0));
That works. But: I'm trying to write some code for math.d and when I put this code inside math.d it doesn't work anymore. Also removing "import std.math" or moving it after the other import, did not help.
Sep 18 2019
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 18 September 2019 at 13:24:05 UTC, berni wrote:
 On Wednesday, 18 September 2019 at 12:37:28 UTC, Simen Kjærås 
 wrote:
 How to resolve this, though? The simplest solution is to not 
 use selective imports:

     import std.math;
     import std.complex;

     writeln(abs(complex(1.0,1.0)));
     writeln(abs(1.0));
That works. But: I'm trying to write some code for math.d and when I put this code inside math.d it doesn't work anymore. Also removing "import std.math" or moving it after the other import, did not help.
So what you have is basically this? import std.stdio; float abs(float f) { return f >= 0 ? f : -f; } unittest { import std.complex : complex, abs; auto a = complex(1.0,1.0); auto b = 1.0f; writeln(abs(a)); writeln(abs(b)); } That does indeed fail to compile, and there's no easy way to introduce the module-level abs() function to the scope. Again though, MergeOverloads to the rescue: float abs(float f) { return f < 0 ? -f : f; } unittest { import std.complex : complex, cabs = abs; alias abs = MergeOverloads!(cabs, .abs); abs(1); abs(complex(1,1)); } template MergeOverloads(T...) { static foreach (E; T) alias MergeOverloads = E; } -- Simen
Sep 19 2019
parent reply berni <someone something.org> writes:
On Thursday, 19 September 2019 at 07:26:17 UTC, Simen Kjærås 
wrote:
 That does indeed fail to compile, and there's no easy way to 
 introduce the module-level abs() function to the scope. Again 
 though, MergeOverloads to the rescue:
I'm not sure, if MergeOverloads will be accepted into std/math.d. handle complex numbers although the algorithm would be identical. Maybe handling complex numbers in math.d can be added later (or maybe it's better to put this in std/complex.d anyway, but then code duplication would be necessary).
Sep 19 2019
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Thursday, 19 September 2019 at 10:25:01 UTC, berni wrote:
 On Thursday, 19 September 2019 at 07:26:17 UTC, Simen Kjærås 
 wrote:
 That does indeed fail to compile, and there's no easy way to 
 introduce the module-level abs() function to the scope. Again 
 though, MergeOverloads to the rescue:
I'm not sure, if MergeOverloads will be accepted into does not handle complex numbers although the algorithm would be identical. Maybe handling complex numbers in math.d can be added later (or maybe it's better to put this in std/complex.d anyway, but then code duplication would be necessary).
You could perfectly well place MergeOverloads inside whatever function you make, thus not polluting std.math: float abs(float f) { return f < 0 ? -f : f; } unittest { import std.complex : complex, cabs = abs; template MergeOverloads(T...) { static foreach (E; T) alias MergeOverloads = E; } alias abs = MergeOverloads!(cabs, .abs); abs(1); abs(complex(1,1)); } (you can also use this to create ridiculous overload sets, since MergeOverloads doesn't care if one function is called abs and the other dropBackExactly. Please don't do this) If you want to introduce MergeOverloads to Phobos officially, std.math is definitely *not* the place though. :) I'd probably say std.functional, but I'm not really sure where it'd belong. Let's put it in std.exception, since it's an exception from D's usual overload rules in functions. :p I don't think anything in std.math explicitly deals with std.complex at this point (some things may handle it generically), so it seems std.complex would be the logical place for anything that does. Might I ask what specifically you're working on? -- Simen
Sep 19 2019
parent berni <someone something.org> writes:
On Thursday, 19 September 2019 at 11:16:12 UTC, Simen Kjærås 
wrote:
 Might I ask what specifically you're working on?
Of course. It's about issue 15881 (and 15763), namely approxEqual not always doing, what people think it should do. (As a side note: I stumbled over this, when I wanted to file a bug report, because I thought it's not doing, what it should do. In my case I was comparing the distance of geographic items in a town. approxEqual decided, that two items which where at opposite ends of the town, are at the same place. Seen from a global perspective, this might indeed be true, but here it was not, what I was looking for.) Well, some people (including me) think, that approxEqual should be a replacement for == for floatingpoint numbers, to accept small errors, that inevitantly creep in. But approxEqual accepts rather large errors with it's default values. (Probably because it's first use was to write some unittests for other functions in phobos, where this is sufficent to make the unittests pass, but not optimal, as errors might stay undetected). As it's not easy to define "small", I searched in the internet, if others have investigated on this and came up with [1], which, in my eyes, is a very good summary. Now I'm working on a replacement for approxEqual with the bit too fast and would have caused quite some trouble, so I suggested by n8sh. [1] https://www.python.org/dev/peps/pep-0485/
Sep 19 2019
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Sep 18, 2019 at 12:37:28PM +0000, Simen Kjrs via Digitalmars-d-learn
wrote:
[...]
 template MergeOverloads(T...) {
     alias MergeOverloads = T[0];
     static if (T.length > 1) {
         alias MergeOverloads = MergeOverloads!(T[1..$]);
     }
 }
 
 I would however label that a horrible hack.
 
 FWIW, I've filed this issue: https://issues.dlang.org/show_bug.cgi?id=20226
[...] Horrible or not, it's very clever. I wouldn't have thought of that! But yeah, the way alias can't be overloaded inside a function body is kinda stupid, esp. seeing that aliasing an overload is exactly how you resolve an analogous problem inside class scope: class Base { int abs(int); } class Derived : Base { float abs(float); // causes ambiguity alias abs = Base.abs; // brings Base.abs into overload set void func() { // now abs(...) will correctly use overload sets } } I would have expected you could do this in function scope as well, and was surprised the compiler rejected it. T -- Famous last words: I wonder what will happen if I do *this*...
Sep 18 2019