Re: impure
On Monday, 8 April 2024 at 07:03:40 UTC, Alexandru Ermicioi wrote: On Sunday, 24 March 2024 at 07:41:41 UTC, Dom DiSc wrote: I'm creating a library that is completely pure, but it doesn't compile with pure: at the top because of one impure unittest (which uses random to test some things only probabilistic)! So do I really need to declare every function pure individually because of a test?!? Can we please have a @impure attribute? And by the way also @throws and @gc? That would make live so much easier... Try `debug unittest {...}`? Cool. This seems to work. That's a nice workaroud for tests. Yay!
Re: impure
On Sunday, 7 April 2024 at 23:32:24 UTC, MrJay wrote: A better way to apply a attribute to an entire file is to use an explicit scope you can still apply this to basically the entire file but leave the tests out of it. Better than an explicit impure (or pure=false) attribute? I don't think so. It heavily uglyfies the file, as single items without a specific attribute are interspersed in the file. So the scope need to end before and start again after the affected function or test. An it stops working at all if e.g. one test is impure and another test is @gc, because then the scopes overlap and can no more be contained in each other. Really, having the counter-attributes would improve the language.
Re: impure
On Sunday, 24 March 2024 at 09:16:20 UTC, Jonathan M Davis wrote: So, yes, you've run into a problem that it would be nice to have a better fix for, but even if we could negate attributes in general, there are good reasons to prefer to avoid mass-applying attributes. I don't see it as "mass-applying attributes" rather than changing the default to something more sane, so that I have to apply *less* attributes to any single function. If the addition of new keywords (like "throws", "@gc" and "impure") is a problem, why not doing it like @nogc(false) or @nogc=false (likewise for nothrow and pure)?
Re: real.sizeof
On Monday, 5 February 2024 at 17:28:38 UTC, Iain Buclaw wrote: Padding. x86 ABI prefers things to be aligned, so on x86 it's 12 bytes, x86_64 16 bytes. In both cases you don't get any extra precision over the 80-bits that x87 gives you. This is exactly what I mean. The ABI may pad it, but sizeof should still give the number of bytes that are really used (not counting the gaps). Or is there a way to change the alignment of basic types? In my code I wanted do decide if the processor uses double-extended or quadruple as real depending on the sizeof. But now I learned I cannot rely on this. Fortunately there is mant_dig, which gives the correct info. At least in an array of real I would expect no padding, like in an array of bool (except for odd length).
Re: struct initializer
On Thursday, 30 November 2023 at 12:15:04 UTC, Dennis wrote: The syntax was inherited from C. The 'special place' is called initialization, and it's special because the target type of the initializer is known in advance This is no different from `S fun(){ return { 5, 2 }; }` It creates a new instance of a struct, and the type is known in advance (it's the return type). So it's not an expression. But this place of initialization is not allowed. Therefor I think calling `S s = { 5, 2 };` 'special' is justified.
Re: struct initializer
On Thursday, 30 November 2023 at 14:10:35 UTC, zjh wrote: On Wednesday, 29 November 2023 at 16:38:36 UTC, Dom DiSc wrote: ```d struct S { int a; int b; } S2 fun3() { return S2( 5, 2 ); } ``` Here,`S2( 5, 2 );` violeit `DRY` principle. Yes. I think if we have the brackets form, it should be allowed here: ```d S Fun(){ return { 5, 2 }; } ``` This IS an initialization and the type is known. Requiring the repetition of the type is also here annoying. So it is not a syntax reserved for initialization, but only for initialization with equals operator. I think this is inconsequent. Either allow it for all initializations, or get rid of it, like DIP 1031 suggested.
Re: struct initializer
On Wednesday, 29 November 2023 at 16:48:09 UTC, Paul Backus wrote: You can use this syntax without an explicit constructor: struct S3 { int a; int b; } S3 fun() { return S3(5, 2); } The language spec calls this a struct literal Ok, so we have ```d struct S { int a; int b; } S s = S(5, 3); // works s = S(6, 2); // works S fun() { return S(5, 2); } // works int fun2(S s2); fun2(S(4,4)); // works ``` but ```d struct S { int a; int b; } S s = { 5, 3 }; // works s = { 6, 2 }; // doesn't work S fun() { return { 5, 2 }; } // doesn't work int fun2(S s2); fun2(S(4,4)); // doesn't work ``` So, why supporting the (somewhat strange looking) version with curly backets at all? It only works in one special place, so is simply overhead to remember. Again a superfluous way to do the same - but only under specific circumstances. I think a syntax should work either always or never.
Re: struct initializer
Sorry, I meant ```d fun2({4, 4}); // doesn't work ```
struct initializer
```d struct S { int a; int b; } S s = { 5, 2 }; // works fine S fun() { return { 5, 2 }; } // doesn't work :-( S fun2() { S s = { 5, 2 }; return s; } // works but is ugly struct S2 { int a; int b; this(int c, int d) { a=c; b=d; } } S2 fun3() { return S2( 5, 2 ); } // works but requires explicit constructor ``` Is there a reason why the short form is not possible? It's clearly an initialization of a new instance of a struct, and the requested type is unambigous (the return type of the function).
Re: interface inference
On Tuesday, 28 November 2023 at 11:01:14 UTC, Antonio wrote: ```d I aOrB(bool check){ if(check) return new A(); else return new B(); } ``` **Is it the expected behaviour for ternary conditional?** Here the compiler knows what type to return (from the function signature). But the ternary operator doesn't know this, so its arguments need to be of the same type (or implicitly convert to a common type).
Re: D: Convert/parse uint integer to string. (@nogc)
On Tuesday, 28 November 2023 at 08:51:21 UTC, Mark Davies wrote: On Friday, 24 November 2023 at 09:35:00 UTC, BoQsc wrote: ``` import std.stdio; char[10] longToString(long n) @nogc ``` For a 'long' 10 characters is likely to be not enough (long max is 9223372036854775808 which has 19 chars, and you should reserve additional one for the sign and one for the terminating null), so I would at least recommend using char[21]. with char[10] your function becomes a big hole in your security, as it can easily be misused to write 10 bytes of freely selectable garbage behind your allocated memory. But as you want to avoid the gc, security might not be a goal for you, so continue living in the 1970's.
Re: How does D’s ‘import’ work?
On Thursday, 1 June 2023 at 03:47:00 UTC, Cecil Ward wrote: I have another question if I may, what do we do about getting makefiles right given that we have imports ? You can replace your whole makefile by calling the compiler with -I (not always, but if you don't do funny things in your makefile). - This ability of the D compiler was just recently discovered (after -I was implemented for other reasons), but it relies on the fact that we have imports.
Re: static immutable that has no initialiser - should this raise an error?
On Tuesday, 30 May 2023 at 04:11:00 UTC, Cecil Ward wrote: static immutable T foo; T bar() { return foo; } Should we get an error from the D compiler here as the initialiser has been forgotten? What do you think ? No. There are no un-initialized values in D. It gets its default value, if no explicit value is given. That's intended, not an error. If you need an un-initialized value, set it =void. Of course, for immutable values that makes no sense at all. But I think, this already gives you an error.
Re: Code duplication where you wish to have a routine called with either immutable or mutable arguments
On Wednesday, 31 May 2023 at 03:29:33 UTC, Cecil Ward wrote: I have to admit that I don’t really understand immutable. I have an idea that it could mean that an object has an address in ROM, so its value will never change. Maybe const doesn’t give you such a strong guarantee, disallows ‘you’ from modifying it but others might do so, but who knows. There are two perspectives: that of the value handed to a function and that of the function taking the value. "immutable" (or "mutable") is a property of the value, "const" is a property of the function. If the function can work with mutable values, but in fact doesn't mutate them itself, it could also work with immutable values. The fact that others could modify your "const" value doesn't matter for immutable values, because they of course can't be modified by others. For the function it doesn't matter, because it only guarantees not to modify it itself, don't care about what other can or can't do. Without a guarantee as strong as the first idea I can’t really understand how const can work properly. "You treat it as const so do not modify it, but it might not be eternally fixed and unchanging" that doesn’t seem to have enough value to me. Why? What guarantee are you missing? Your function can work with mutable data, so you don't care if it can be modified also by others. Now it happens that you doesn't modify the data. So why shouldn't you be able to work on data that guarantees that it also will not be changed by others? You don't care for such modification anyway. The meaning of "immutable" is: I cannot be modified. Not by you and not by anybody else. It's a property of a value. The meaning of "mutable" is: I can be modified by anybody. Work with me only if that is ok for you. It's a property of a value. The meaning of "const" is: I don't care if others modify the data or not, I won't modify it myself. It's a property of a function.
Re: Concepts like c++20 with specialized overload resolution.
On Saturday, 27 May 2023 at 19:16:18 UTC, vushu wrote: It depends. This example is quite small and a `static if` is sufficient, if you have a lot of cases it would make sense to split thing up into overloaded functions. Even with a lot of cases you can simply call in each static if a (private) subfunction. I would always prefer this over overloads because it gets you a much cleaner API (I consider the constraint to be part of the function signature, which therefore can get very long and produces a lot of overloads that each only differ in the constraints). Also it becomes more and more obfuscated for which cases there is an overload and for which not. A good (?!?) example of how bad this can get is the "to" template.
Re: Proper way to handle "alias this" deprecation for classes
On Friday, 12 May 2023 at 15:00:48 UTC, Salih Dincer wrote: ```d struct Fraction { int num, den; this(int n, int d) { num = n; den = d; } // Cast Expression : convert float value of fraction auto opCast(T : float)() const { return cast(float)(num) / cast(float)(den); } } ``` If you want auto-conversion, you should be more explicit: ```d float opCast() { } ``` because if you return "auto" it is not the highest-prio fit and therefore not chosen. If you have multiple choices, you still don't need to use "auto". Instead you can return T: ```d T opCast(T)() if(isFloatingPoint!T) { return cast(T)num / cast(T)den; // float, double or real } ``` Kind of ironic, but especially "auto" does NOT fit automatically :-)
Re: learning D as your first language but having difficulty in making or logic building in order to make software
On Tuesday, 11 April 2023 at 14:13:17 UTC, slectr wrote: I want to make software like krita inkscape and my own language using D Although i previosly learned C and C++ i left it in the middle and for some reasons i dont want to learn those so i searched for alternatives and found D but there are not a lot of resources like cookbooks and videos as compared to C or C++ does anybody know any resources for making complex software in D for some practise ? Of course there are cookbooks - one even has explicitly that title! Have a look on forum.dlang.org under Resources/Books.
Re: How to use @safe when a C library integration needed
On Monday, 23 January 2023 at 16:36:21 UTC, Leonardo wrote: Hello. How to use @safe when a C library integration needed? Everything need a system function... ```d @safe fn() { // lot of safe stuff () @trusted { // in this block[*] @system function like extern C can be called. // you need to make sure the API is used correct @assert(/*C_Fun is safe to be used with param1*/); @assert(/*C_Fun is safe to be used with param2*/); C_Fun(param1, param2); }(); // more safe stuff } ``` [*] in fact, this is a lambda function that is directly called, because real trusted blocks are not allowed (yet).
Re: Is there a way to enforce UFCS?
On Wednesday, 4 January 2023 at 14:21:46 UTC, bauss wrote: ```d class Foo { int bar; void setBar(Foo foo, int value) { foo.bar = value; } } void main() { foo.setBar(100); // Not UFCS - just method call to the class foo.setBar = 100; // Not UFCS - simply a setter function call (equal to the above) setBar(foo, 100); // Error } ``` I think this is really another usecase for @property: we should forbid the function call syntax for them (so one needs to use them like a variable). This is useful to enable library authors to enforce this, so that if a property is replaced by a variable (e.g. during refactoring), it's not a braking change for the users of the library. Else it could be that setters or getters are directly called, which would not compile anymore with a variable (that doesn't have getters or setters). @properties are intended to be used like variables - the only differences (and the reason why they exist) is the read or write protection they provide, and that they may be calculated on the fly (not stored in memory at all). That they are realized with a construct that looks similar to (a pair of) functions should be completely transparent for the user of a library. Properties are not functions. If you want a function, use a function. If @properties would be the same as functions, they are superfluous garbage. Either make something useful out of them or remove them.
Re: Fix template parameter
On Tuesday, 9 August 2022 at 22:58:16 UTC, Paul Backus wrote: On Tuesday, 9 August 2022 at 22:36:23 UTC, Dom Disc wrote: On Tuesday, 9 August 2022 at 22:32:23 UTC, Dom Disc wrote: On Tuesday, 9 August 2022 at 21:16:22 UTC, Paul Backus wrote: Yes, this syntax allows anything that implicitly converts to `BigInt`; Oh, or do you mean I will get two different instances of the template, if I call it with two different types with implicit conversion? That would make it even worse than the non-templated declaration! Yes, exactly. Ok, then I consider this is a bug in Phobos that should be corrected. All instances of ```D foo(T : fixedType)(T x) { } ``` should be replaced by ```D foo(fixedType x) { } ```
Re: Fix template parameter
On Tuesday, 9 August 2022 at 22:32:23 UTC, Dom Disc wrote: On Tuesday, 9 August 2022 at 21:16:22 UTC, Paul Backus wrote: Yes, this syntax allows anything that implicitly converts to `BigInt`; Oh, or do you mean I will get two different instances of the template, if I call it with two different types with implicit conversion? That would make it even worse than the non-templated declaration!
Re: Fix template parameter
On Tuesday, 9 August 2022 at 21:16:22 UTC, Paul Backus wrote: Yes, this syntax allows anything that implicitly converts to `BigInt`; for example: ```d import std.bigint; void fun(T : BigInt)(T t) { pragma(msg, "Instantiated with T = `" ~ T.stringof ~ "`"); } struct S { BigInt n; alias n this; } void main() { S s; fun(s); // Instantiated with T = `S` } Aha. But isn't that also true for the other declaration? ```d void fun(BigInt t) { } ``` will also accept anything that implicitly converts to BigInt, no? So I still don't see the big difference. Except that is is a template - for what ever that may be useful if it doesn't take more than one type. Relying on something as subtle as this difference does have a code-smell for me.
Re: Fix template parameter
On Monday, 8 August 2022 at 12:46:48 UTC, bauss wrote: On Monday, 8 August 2022 at 12:02:02 UTC, Dom Disc wrote: ```D pure @nogc @safe BigInt opAssign(T : BigInt)(T x); ``` This will only be included in the object file if used. ```D pure @nogc @safe BigInt opAssign(BigInt x); ``` This will always be in the object file. Ah, ok. But shouldn't the linker throw it out of an executable, if it is not used? I mean, even the most dump linker should be able to do this basic optimization...
Fix template parameter
Hello. I found in the documentation functions declared like this: ```D pure @nogc @safe BigInt opAssign(T : BigInt)(T x); ``` What is the difference to declaring it like: ```D pure @nogc @safe BigInt opAssign(BigInt x); ``` To me the first declaration seems to be unnecessarily bloated, so I ask myself: does it provide any kind of advantage? I can't see it.
Re: How do I initialize a templated constructor?
And then you can instantiate it with ```D auto val = TestArray!10(ubyte(60)); // if you want type to be ubyte ```
Re: How do I initialize a templated constructor?
On Monday, 8 August 2022 at 06:58:42 UTC, bauss wrote: On Monday, 8 August 2022 at 05:38:31 UTC, rempas wrote: In the following struct (as an example, not real code): ``` struct TestArray(ulong element_n) { int[element_n] elements; this(string type)(ulong number) { pragma(msg, "The type is: " ~ typeof(type).stringof); } } ``` You cannot do this. But if you only want to know the type of the parameter, you can do this: ```D struct TestArray(ulong element_n) { int[element_n] elements; this(type)(type number) { pragma(msg, "The type is: " ~ type.stringof); } } ```
Re: Verbosity in D
On Monday, 8 August 2022 at 00:40:11 UTC, TTK Ciar wrote: On the other hand, I've noticed that D's idiomatic brevity can be diluted by the extremely verbose function names used in the standard library. For long function names you can define short aliases, for syntax you can't. So having short and efficient language constructs is by far more important than short function names. Especially a standard-library is much better off having descriptive and unique names - which sort of requires them to be long.
Re: Arbitrary precision decimal numbers
On Saturday, 6 August 2022 at 11:25:28 UTC, Dom Disc wrote: I once did a completely inline implementation of xlcmplx Sorry, one function is NOT inline: ```C class xlcmplx { uint32 Decimal(char* s, uint32 max) const; // string representation } ``` But you should forget about that, because D has much better methods to create a string. In fact good enough that also this function could have been inline... but in D the whole concept of headers is superfluous, so who cares.
Re: Arbitrary precision decimal numbers
On Friday, 5 August 2022 at 14:25:39 UTC, Ruby The Roobster wrote: I'm currently working on an arbitrarily precise division algortihm based off of the done-by-hand standard algorithm, but I need to get it to work for complex numbers, [which it's just not](https://forum.dlang.org/post/czidpbdywsohstyvi...@forum.dlang.org). I once did a completely inline implementation of xlcmplx based on an arbitrary precision float (in this context called xlfloat) in C++. You only need to exchange xlfloat with your floatingpoint type: ```C class xlcmplx { xlfloat Re, Im; public: friend inline void swap(xlcmplx& a, xlcmplx& b) { swap(a.Re, b.Re); swap(a.Im, b.Im); } xlcmplx(const xlfloat& re =0, const xlfloat& im =0) : Re(re), Im(im) { } xlcmplx(const xlcmplx& c) : Re(c.Re), Im(c.Im) { } // two special "constructors": friend inline xlcmplx i(const xlfloat& x =1) { return xlcmplx(0,x); } // make pure imaginary number friend inline xlcmplx polar(const xlfloat& arg, const xlfloat& norm =1) { return (!norm || !arg) ? norm : xlcmplx(cos(arg),sin(arg)) *= norm; } uint32 Decimal(char* s, uint32 max) const; // string representation inline xlcmplx& operator=(const xlcmplx& c) { if(this != ) Re = c.Re, Im = c.Im; return *this; } inline xlcmplx& operator=(const xlfloat& f) { Re = f, Im = 0; return *this; } inline xlcmplx& operator+() { return *this; } // dummy operator inline xlcmplx operator-() const { return xlcmplx(-Re, -Im); } inline xlcmplx& operator+=(const xlcmplx& c) { Re += c.Re; Im += c.Im; return *this; } inline xlcmplx& operator+=(const xlfloat& f) { Re += f; return *this; } inline xlcmplx& operator-=(const xlcmplx& c) { Re -= c.Re; Im -= c.Im; return *this; } inline xlcmplx& operator-=(const xlfloat& f) { Re -= f; return *this; } inline xlcmplx& operator*=(const xlcmplx& c) { xlfloat t = Re; t *= c.Re; t -= Im*c.Im; Im *= c.Re; Im += Re*c.Im; Re = t; return *this; } inline xlcmplx& operator*=(const xlfloat& f) { Re *= f; Im *= f; return *this; } inline xlcmplx& operator/=(const xlcmplx& c) { return *this *= inv(c); } inline xlcmplx& operator/=(const xlfloat& f) { Re /= f; Im /= f; return *this; } inline bool operator==(const xlcmplx& c) const { return Im == c.Im && Re == c.Re; } inline bool operator==(const xlfloat& f) const { return !Im && Re == f; } inline bool operator!=(const xlcmplx& c) const { return Im != c.Im || Re != c.Re; } inline bool operator!=(const xlfloat& f) const { return !!Im || Re != f; } friend inline const xlfloat& real(const xlcmplx& c) { return c.Re; } friend inline const xlfloat& imag(const xlcmplx& c) { return c.Im; } friend inline xlfloat norm(const xlcmplx& c) { return c.Im ? c.Re ? sqr(quad(c.Re)+quad(c.Im)) : abs(c.Im) : abs(c.Re); } friend inline xlfloat arg(const xlcmplx& c) { return atan2(c.Re, c.Im); } // an angle in [0..2*pi[ friend inline xlcmplx conj(xlcmplx c) { c.Im.neg(); return c; } friend inline xlcmplx inv(const xlcmplx& c) { xlfloat t = quad(c.Re); t += quad(c.Im); return conj(c) /= t; } friend inline xlcmplx quad(const xlcmplx& c) { return xlcmplx(quad(c.Re)-quad(c.Im),(c.Re*c.Im)<<1); } friend inline xlcmplx exp(const xlcmplx& c) { return c.Im ? xlcmplx(cos(c.Im), sin(c.Im)) *= exp(c.Re) : exp(c.Re); } friend inline xlcmplx sin(const xlcmplx& c) { return c.Im ? c.Re ? xlcmplx(sin(c.Re)*cosh(c.Im), cos(c.Re)*sinh(c.Im)) : i(sinh(c.Im)) : sin(c.Re); } friend inline xlcmplx cos(const xlcmplx& c) { return c.Im ? c.Re ? xlcmplx(cos(c.Re)*cosh(c.Im), sin(c.Re)*sinh(c.Im)) : cosh(c.Im) : cos(c.Re); } friend inline xlcmplx ln(const xlcmplx& c) { return xlcmplx(ln(norm(c)), arg(c)); } // main value - multiples of 2*pi*i added are also valid results }; inline bool operator==(const xlfloat& f, const xlcmplx& c) { return c == f; } inline bool operator!=(const xlfloat& f, const xlcmplx& c) { return c != f; } inline xlcmplx operator+(xlcmplx a, const xlcmplx& b) { return a += b; } inline xlcmplx operator+(xlcmplx a, const xlfloat& b) { return a += b; } inline xlcmplx operator+(const xlfloat& a, xlcmplx b) { return b += a; } inline xlcmplx operator-(xlcmplx a, const xlcmplx& b) { return a -= b; } inline xlcmplx operator-(xlcmplx a, const xlfloat& b) { return a -= b; } inline xlcmplx operator-(const xlfloat& a, const xlcmplx& b) { return -b += a; } inline xlcmplx operator*(xlcmplx a, const xlfloat& b) { return a *= b; } inline xlcmplx operator*(xlcmplx a, const xlcmplx& b) { return a *= b; } inline xlcmplx operator*(const xlfloat& a, xlcmplx b) { return b *= a; } inline xlcmplx operator/(xlcmplx a, const xlcmplx& b) { return a /= b; } inline xlcmplx operator/(xlcmplx a, const xlfloat& b) { return a /= b; } inline xlcmplx operator/(const xlfloat& a, const xlcmplx& b) { return xlcmplx(a) /= b; } inline xlcmplx pow(const xlcmplx& c, const int exp) { return polar(arg(c)*exp, pow(norm(c), exp)); } inline xlcmplx pow(const xlcmplx& c, const
Re: Compiler switch for integer comparison/promotion to catch a simple error
On Sunday, 29 May 2022 at 01:35:23 UTC, frame wrote: Is there a compiler switch to catch this kind of error? ```d ulong v = 1; writeln(v > -1); ``` IMHO the compiler should bail a warning if it sees a logic comparison between signed and unsigned / different integer sizes. There is 50% chance that a implicit conversion was not intended. We have a solution for this problem in bugzilla: https://issues.dlang.org/show_bug.cgi?id=259 This would allow for the above compare to simply work with all integer types signed or unsigned and produce always the correct result. It was rejected because it would break old code that rely on this misbehaviour (and even worse: the behaviour would be different from what C does, oh my god!!)
Re: What are (were) the most difficult parts of D?
On Tuesday, 17 May 2022 at 06:28:10 UTC, cc wrote: having had this leave a pretty bad taste in my mouth, I now avoid the GC whenever possible, even when I don't have to. Bad idea. You should only avoid GC if your profiling shows that it is bad in a specific piece of code (e.g. some inner loop that's called millions of times - the real bottlenecks). This frees you from the mental load of thinking about memory allocation at all unless it becomes necessary, which it will actually be only in very, very few places. Better invest your precious time to really DO profiling and find out where the bottlenecks are! Maybe a run-and-done program can get along just fine allocating everything to the GC. But maybe I'll need to modularize it some day in the future and call it from another program with far more intensive requirements that doesn't want superfluous data being added to the GC every frame. Yes - and when you modularize your program, keep in mind that a module should avoid allocating anything large or often on its own at all. It should better work on memory given to it. So you remain able to decide on the calling side if you allocate by GC (default) or (if something turns out to be a bottleneck) manually allocate memory. Far better to just keep your house clean every day than let the trash pile up and wait for the maid to come, IMO. Inevitably it's going to be her day off when you have guests coming over. There's always gc.collect, which calls the maid to do her job, even if she is on vacation. Do so whenever you think you may have piled on a lot of trash and are out of house (have the time available that a collection may take). This way you can be sure there is never too much trash, so the GC is very unlikely to ever disturb you when there is no time for long collection cycles.
Re: Question on shapes
On Tuesday, 17 May 2022 at 09:30:12 UTC, forkit wrote: On Tuesday, 17 May 2022 at 04:37:58 UTC, Ali Çehreli wrote: In you OOP example, I am curious why you chose Shape to be an interface, rather than a base class. You can inherit from multiple interfaces, but only from one base class. So if you need multiple inheritance, better use interfaces. Especially at the first level of objects I would almost always use only interfaces, no classes. I consider this better design.
Re: Why do immutable variables need reference counting?
On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote: 1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that: void foo (const(int)[] arr); 2) This variable is const: const i = 42; Well, there is the confusion: There is no "reference" in the second case at all! I think this second case should not be allowed. Use immutable i = 42; instead. The meaning is identical, but we could remove the burden of two different meanings from const if it is not allowed. const should only be allowed in function declarations. A variable must be declared either mutable or immutable. It's only functions that may guarantee not to modify a parameter or the objects they belong to, and so are allowed to work on both mutable and immutable objects.
Re: abs and minimum values
On Friday, 29 October 2021 at 14:20:09 UTC, Ali Çehreli wrote: Unsigned!T abs(T)(const(T) x) if(isIntegral!T) { static if(isSigned!T) if(x < 0) return cast(Unsigned!T)-x; return x; } void main() { int a = -5; int b = -4; writeln(a + abs(b)); // -5 + 4 == -1? (No!) } The program prints uint.max. This should be no surprise. You need to know what the resulting type of int + uint should be. And it is .. uint! which is one of the stupit integer-promotion rules inherited from C. I just don't understand how "promoting" something by dropping some important information (the sign) and on the fly also destroying the absolute value can ever be a good choice. I always thought it should be the other way round. The way it is is like "promoting" int + float to int (by discarding the fraction part and possibly too high exponents). And those two types are also of same size, so this is not an argument. Promotion should always be in a direction where it at least sometimes can be correct.
Re: abs and minimum values
On Friday, 29 October 2021 at 08:33:07 UTC, Imperatorn wrote: Imo abs should never be able to return a negative value Yes, but phobos defines it to return a signed type, so theoretical it can return negative values. And they won't change that. I really think it should return an unsigned type, but that's a "breaking change". Bullshit policy!
Re: abs and minimum values
On Thursday, 28 October 2021 at 21:26:04 UTC, kyle wrote: Okay I checked the phobos docs and it does say "Limitations Does not work correctly for signed intergal types and value Num.min." Should have looked there first, I know. Still seems pretty silly. I recommend to implement your own abs function this way (was not accepted for phobos, as it now does NOT return the same type as the argument, which was considered a "breaking change" :-( ): ```D /// get the absolute value of x as unsigned type. always succeeds, even for T.min Unsigned!T abs(T)(const(T) x) if(isIntegral!T) { static if(isSigned!T) if(x < 0) return cast(Unsigned!T)-x; return x; } ```