Re: Why can't we transpile C++ to D?
On Thursday, 10 June 2021 at 15:09:02 UTC, Tejas wrote: Sorry, I'm rather ignorant when it comes to this, but why can't we use [pegged](https://github.com/PhilippeSigaud/Pegged) to transpile C++ code to D? See https://stackoverflow.com/questions/14589346/is-c-context-free-or-context-sensitive#answer-14589567 Mixing semantic with parsing isn't necessarily a good idea. Then we won't need a nogc compatible std library and so many other things could get easier, like getting legacy code to use Dlang. That doesn't solve the ABI compatibility to C++, only if source is available, but they may be compiled with compilers which can't build anymore with the current tool chain at least without to rebuild the tool chain. It might not be worth it for C+17 and beyond, but older codebases could benefit significantly, right? The problem is that old looking code doesn't become modern when it is transpired to D as the concepts to do things have changed.
Re: How to work around the infamous dual-context when using delegates together with std.parallelism
On Thursday, 27 May 2021 at 12:58:28 UTC, Christian Köstlin wrote: That looks nice, but unfortunately my data for servers and users in the real world is not static but comes from a config file. Okay, but then parametrizing the static lambda with runtime parameters should work. The important fact is that the closure needs to be static.
Re: How to work around the infamous dual-context when using delegates together with std.parallelism
On Thursday, 27 May 2021 at 12:17:36 UTC, Christian Köstlin wrote: Can you explain me, where here a double context is needed? Because all data now should be passed as arguments to amap? Kind regards, Christian I believe D's type system isn't smart enough to see independence between context and closure, otherwise your original example would also work as users and servers are context independent. What about: ```D string doSomething(string[] servers, string user) { return user ~ servers[0]; } void main() { static servers = ["s1", "s2", "s3"]; static users = ["u1", "u2", "u3"]; static lambda = (string user) => servers.doSomething(user); writeln(map!(user => servers.doSomething(user))(users)); writeln(taskPool.amap!(lambda)(users)); } ```
Re: How to work around the infamous dual-context when using delegates together with std.parallelism
On Thursday, 27 May 2021 at 09:58:40 UTC, Christian Köstlin wrote: I have this small program here test.d: ``` import std; string doSomething(string[] servers, string user) { return user ~ servers[0]; } void main() { auto servers = ["s1", "s2", "s3"]; auto users = ["u1", "u2", "u3"]; writeln(map!(user => servers.doSomething(user))(users)); writeln(taskPool.amap!(user => servers.doSomething(user))(users)); } ``` I think it relates to https://issues.dlang.org/show_bug.cgi?id=5710 The reason is that amap requires a this pointer of type TaskPool and a context pointer to the closure which belongs to main, at least because it requires servers. Having both isn't possible due to problems in non DMD compilers. If you rewrite it more statically: ```D string doSomething(string[] servers, string user) { return user ~ servers[0]; } string closure(string user) { return servers.doSomething(user); } auto servers = ["s1", "s2", "s3"]; int main() { auto users = ["u1", "u2", "u3"]; writeln(map!(user => servers.doSomething(user))(users)); writeln(taskPool.amap!(closure)(users)); return 0; } ``` PS: Just enable markdown if you want to highlight D code
Re: Why is this allowed? Inheritance variable shadowing
On Wednesday, 26 May 2021 at 18:58:47 UTC, JN wrote: I am not buying the "C++ does it and it's legal there" argument. A point for it is the consistency with methods which also redefine super methods as default strategy. The question is if the default strategy needs to be changed? I wouldn't argue so as overriding super methods/fields as default is much more dangerous as it might destroy the super class's semantics. What about explicitly tagging fields with override instead, then it would be a compile error if the base class hasn't the tagged fields.
Re: How does inheritance and vtables work wrt. C++ and interop with D? Fns w/ Multiple-inheritance args impossible to bind to?
On Tuesday, 25 May 2021 at 02:47:19 UTC, Gavin Ray wrote: The below seems to work at least, which is encouraging: Awesome! At least, it becomes problematic with fields in base classes, it would be nice if we could map them to @property annotated functions in D interfaces.
Re: How does inheritance and vtables work wrt. C++ and interop with D? Fns w/ Multiple-inheritance args impossible to bind to?
On Monday, 24 May 2021 at 17:39:38 UTC, Gavin Ray wrote: I'd be grateful for solid information on this Here is a more informal report how it works in C++: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.23.4735&rep=rep1&type=pdf But in the end, I think a byte code analysis is needed to be sure. The best tools to visualize have already been proposed by you, so I think you are on a good path.
Re: How does inheritance and vtables work wrt. C++ and interop with D? Fns w/ Multiple-inheritance args impossible to bind to?
On Monday, 24 May 2021 at 17:39:38 UTC, Gavin Ray wrote: Hence why I was asking how to make D structs/classes that have compatible or identical vtables to multiply inherited objects to pass as arguments to `extern (C++)` functions. I think classes annotated with extern is your only high level guaranteed == type safe option to be compatible to other c++ classes. But you seek for the general multiple inheritance case which seems not to be supported with `extern`, sadly. So you stick with manual solutions like template metaprogramming or/and raw pointer fiddling. Anyway, both solutions would require an unsafe cast in the end, so you are on your own.
Re: ugly and/or useless features in the language.
On Sunday, 23 May 2021 at 08:35:31 UTC, Tony wrote: Why is metaprogramming added features better than the same features added in the language? One is standard between entities, the other is not. Some points: - Some features aren't general enough to be added as builtin but make sense to have them in libraries - library features could be extended and modified arbitrarily which isn't possible with builtin features - if better library features are found they can simply be removed from the library while probably breaking the library, it doesn't break the language which is much of a bigger problem - most problems with builtin features is that they overwrite grammar for a certain scope a.k.a syntactic sugar leading to learn new rules/a new language (see C++) while a general framework would reuse the same grammar again and again providing uniformity - most builtin features are added incrementally to a language in order to be predictable/implementable, unfortunately this leads often to fragmentation because later with enough practical experience you see another approach to solve the problem, making this approach available may overlap with the feature you already added, i.e. it solves to some extent the same problems but with other tradeoffs making it hard to choose the right approach. There are also disadvantages, among them the locality/globaility problem: Implementing features via macros may need context information and other existing features need to be informed of the new feature in order to handle it correctly which would require many hooks which can't be solved solely with macros. We would require massive compiler reflection support for this. Further, it is unclear how it would behave at boundaries where hooks would collide with each other.
Re: ugly and/or useless features in the language.
On Saturday, 22 May 2021 at 13:31:45 UTC, Ola Fosheim Grøstad wrote: The D AST is not really suitable as it stands. D is a bit like C++ in this regard, there might be a minimal core language that could be distilled from it, but it would take a D3 full breaking change to get there, so it won't happen. Well, I think D without templates is already a lightweight core language. The real cost comes with the template language. But I think providing an external ast tree mapped onto the changing internal one used by DMD would be a feasible approach.
Re: ugly and/or useless features in the language.
On Saturday, 15 May 2021 at 14:31:08 UTC, Alain De Vos wrote: Which parts in dlang don't you use and why ? Well, I don't like magic constructs in the language like the type of AliasSeq you can't touch. But the more general problem in D are not features per se, but how they are composed of. For instance: Why no AST macros instead of string mixins, templates, mixin templates and alias? All these forms could be special ast macros. Structs are nice but at the same time awful to use because they are incompatible with interfaces and classes, I hope this will change to some extent, but I think it wouldn't be that smooth. Being multi-paradigmatic seems nice at a first sight, but which paradigm should your standard library and other frameworks follow? Providing all paradigms at once make your frameworks redundant and bloated, so you just follow one which is most of the time not OOP. This is the same problematic with custom memory containers. I wish we would only use structs as ref/value types with the ability to box them to interfaces automatically increasing compatibility between libraries to a large extent.
Re: dlang vs crystal-language
On Wednesday, 28 April 2021 at 22:41:03 UTC, Alain De Vos wrote: What are the strengths and weaknesses comparing the two languages ? I can name a strength of dlang is the working binding to tk and gtk. Just to say, Crystal is a neat language, feels like a static ruby. Strengths: - Poweful type inference - lightweight julian/ruby like syntax - nice lightweight macros, don't know if they were a good fit for D - nice yield builtins - feels like a lightweight Java - nice high level feeling and is GCed Weaknesses: - as I said, it is hard to understand when type inference is used completely everywhere, said that this is crystals killer feature - slow compilation time just because of the "type inferred everywhere" design - the OOP system lacks specific features from other OOP systems - hasn't the same support for low level programming as in D or Nim - is relative unknown, although I don't know why.
Re: write once type?
On Friday, 30 April 2021 at 13:36:10 UTC, Steven Schveighoffer wrote: I could have looped 2x over the args, and used an inner function to fetch the first one (or maybe used a staticIndexOf to get the first ColumnDef thing), and a second loop to verify all the remaining args have the same table def, but this loop works just as designed and is super-readable. I would say likewise using static indexing, is it really that slow? Otherwise, iterate over Pair-like partitions would also be a solution and compare if first.tableDef is second.tableDef, don't know if D has something for this statically available. Further, I would like to refactor the static if out to an in-requirement, though I'm unsure if in-assertions support arbitrary exception types.
Re: dlang vs crystal-language
On Thursday, 29 April 2021 at 22:47:08 UTC, Alain De Vos wrote: What is the importance of type-annotations in which cases. Specifying invariants which becomes very important when refactoring code. Further it aids as documentation to understand the structures behind. @X @Y @Z makes code sometimes unreadable. You mean something like `List!(HashMap!(String,T))`, yes, but I like it in productive code. It's often the case that verbosity becomes a plus in productive software development, as more people have to read code that to write it, a reason why Java is so successful Sometimes there is a good reason Mostly for prototyping and obvious cases. But the most important point of that is to write faster, I wish the IDE would help for this instead of the compiler's inference. I even think D doesn't even profit very well for inferred types, you need anyway to specify auto which only saves work for large type(application)s. But when they are large, you probably want to prefer annotating the type for the variable. Instead, I want to point to some interesting solution C# offers to save typing and also being explicit about the type: https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/#target-typed-new-expressions In fact, one could imagine that they overload the new operator (at compiler level) to infer the type from the call site type annotation.
Re: write once type?
On Friday, 30 April 2021 at 01:30:54 UTC, Steven Schveighoffer wrote: In my case, for value of a certain type in the loop, I was storing a specific field from the first one I found, and then verifying that all the other values of that type (not exactly the same type, but similar) had the same value for that field, otherwise it was an error. I could have done it in 2 loops, but it's wasteful. I have problems to grok exactly what you're talking about, can you provide some neat and small example for this?
Re: write once type?
On Tuesday, 20 April 2021 at 19:56:33 UTC, Steven Schveighoffer wrote: Not only that, but I may also want to keep processing the loop and do something different if the value has already been set instead of returning immediately, which necessitates a second loop. Can I ask why you require to handle it in one loop? As I see it, you are required to poll over isSet/isNotSet in every iteration.
Re: dlang vs crystal-language
On Wednesday, 28 April 2021 at 22:41:03 UTC, Alain De Vos wrote: What are the strengths and weaknesses comparing the two languages ? I can name a strength of dlang is the working binding to tk and gtk. Less type annotations, fewer guarantees, less compile performance as cons.
Re: Why many programmers don't like GC?
On Thursday, 14 January 2021 at 13:08:06 UTC, Ola Fosheim Grøstad wrote: Because Java has a well defined virtual machine with lots of restrictions. So you're insisting this isn't possible in D?
Re: Why many programmers don't like GC?
On Thursday, 14 January 2021 at 11:11:58 UTC, Ola Fosheim Grøstad wrote: I know your response is *tongue in cheek*, but I actually find it easier to use c++11 style memory management across the board than mixing two models. But this is already the case for C++ and Rust. Remembering the days back developing in C++ there were a huge amount of memory deallocation side effects because opencv's memory management differs from qt's memory management. Just to say it was a hell. Personally, I find it better to prefer encapsulating manual memory management and not to leak them outside.
Re: Why many programmers don't like GC?
As other people already mentioned, garbage collection incurs some amount of non-determinism and people working in low level areas prefer to handle things deterministically because they think non-deterministic handling of memory makes your code slow. For example rendering in gaming gets paused by the GC prolonging the whole rendering process. The conclusion arrives that code runs slow, but it doesn't. In fact, a tracing GC tries to do the opposite, in order to make the whole process faster, it releases memory in a buffer like mode often yielding faster execution in the long run, but not in the short run where it is mandatory not to introduce any kind of pauses. So, in the end, it isn't a fate about performance rather a fate of determinism vs non-determinism. Non-determinism has the potential to let the code run faster. For instance, no one really uses the proposed threading model in Rust with owned values where one thread can only work mutably on an owned value because other threads wanting to mutate that value have to wait. This works by creating a deterministic order of thread execution where each thread can only work after the other thread releases its work. This model gets often praised in Rust but the potential seems only a theoretical one. As a result you most often see the use of atomic reference counting (ARC) cluttering in codebases in Rust. The other point is the increased memory footprint because you have a runtime memory manager taking responsibility over (de)allocation which is impossible to have in some areas of limited memory systems. However, why just provide a one size fits all solution when there are plenty of GC algorithms for different kinds of problem domains? Why not offering more than one just as it is the case in Java? The advantage hereby is to adapt the GC algorithm after the program was compiled, so you can reuse the same program with different GC algorithms.
Re: Why D functions paramter can not implicit infer type of Variant?
On Wednesday, 13 January 2021 at 19:38:55 UTC, Paul Backus wrote: That's what Variant is--a struct that models the universal supertype (sometimes called "Top" or "Any"). Ahh right. Good point, so it already fits.
Re: Why D functions paramter can not implicit infer type of Variant?
On Wednesday, 13 January 2021 at 16:17:02 UTC, Marcone wrote: import std; void a(int b){ } void main() { Variant c = 10; a(c); // Error } Need more sugar. Two problems: 1.) Variant is library defined, compared to the language level there isn't a default strategy to choose int32 here, it could be also, short, long, unsigned ... not to mention all the alias this types. Though it may be possible to define a default strategy but part of the problem is how to tell D to init the type parameters appropriately. 2.) c is mutable, what is if you return c and assign other values of other types to it? Determining all possibilities by traversing following assignments leads to global type inference which no one would ever want to have especially with support of subtyping. A more natural conclusion would be to infer c to the most common supertype as other inferences would unnecessarily exclude future assignments to c. But the most common supertype doesn't seem to exist, and I'm unsure if this type can be modeled at all in D?
Re: I want to create my own Tuple type
What about this? No magic, but I don't know the performance impact. ``` import std.meta; import std.conv; template same(Types...) { static if (Types.length >= 2) { static if (is(Types[0] == Types[$ - 1])) { const same = same!(Types[1 .. $]); } else { enum bool same = false; } } else { enum bool same = true; } } struct Tuple(Types...) { static if (same!Types) { public Types[0][Types.length] elements; public this(Types[0][Types.length] elements...) { this.elements = elements; } } else { static foreach (int i, T; Types) { mixin("public " ~ T.stringof ~ " " ~ "elem" ~ i.stringof ~ ";"); } public this(Types elements) { static foreach (int i, T; Types) { mixin("this.elem" ~ i.stringof ~ "=" ~ "elements[i]" ~ ";"); } } } } int main() { import std.stdio; auto homogenousTuple = Tuple!(int, int)(2, 3); writeln("homogenous tuple ", homogenousTuple.elements[0], ":", typeid(homogenousTuple.elements[0]), ":", homogenousTuple.elements[1], ":", typeid(homogenousTuple.elements[1])); auto heterogenousTuple = Tuple!(int, float)(2, 3); writeln("heterogenous tuple ", heterogenousTuple.elem0, ":", typeid(heterogenousTuple.elem0), ":", heterogenousTuple.elem1, ":", typeid(heterogenousTuple.elem1)); return 0; } ``` Problem is, the type arguments getn't inferred.
Re: [Understanding] Classes and delegate inheritance vs function pointers
On Saturday, 9 January 2021 at 18:16:04 UTC, Q. Schroll wrote: This makes no sense in my head. Is there some reason I'm unable to see? And yes, I think it should work likewise for function pointers, too.
Re: [Understanding] Classes and delegate inheritance vs function pointers
On Sunday, 10 January 2021 at 00:44:26 UTC, Q. Schroll wrote: Car[] cars = ...; Vehicle[] vehicles = cars; vehicles[0] = new Boat; // err... // cars[0] is a Boat now!? Now what? Do what Java does and throw an Exception? Or use const to prevent mutation? Yes, you are right: ``` Derived derived; Base* bp=&derived; *bp=base; derived.method() //ouch ``` I'm unsure what you mean. I mean D doesn't support co(ntra)varaince out of the box, but seems allowing them in certain cases increasing the degree of confusion just as it is the case here. I think it would be just better to generally incorporate co(ntra)variance into D's semantic or completely disregard it.
Re: [Understanding] Classes and delegate inheritance vs function pointers
On Saturday, 9 January 2021 at 20:20:38 UTC, Q. Schroll wrote: That's not what I mean. You copy the reference. That's not what referencing meant. Derived d = new Derived(); Base* bp = &d; // fails const(Base) cbp = &d; // compiles. Generally, allowing covariant assignment for the Ptr, i.e. T*, type only in case of const T* strikes me. IMHO, both should be synchronously (dis)allowed. Because D doesn't support co(ntra)variance, it should be both disallowed? Otherwise, because D already support the const case, why not the non const case? Are there any alignment issues supporting the non const case?
Re: DConf talk : Exceptions will disappear in the future?
On Thursday, 7 January 2021 at 19:35:00 UTC, H. S. Teoh wrote: Whether or not something is inlined has nothing to do with what line number it was written in. Okay, I've tried it out, and it seems it isn't the problem in the binary case as the code was first compiled and the inlined, so the line number is correct in that case. For source code inlining however, the direction is simply opposite. Therefore, compilation of inlined code have to respect the original line number pointing to the throw statement in the inlined function. I think D can handle this, I hope so. "Debugging information" can be included in production code. Yes, but exception line numbers aren't debug infos rather they are passed implicitly as argument to the exception class constructor.
Re: DConf talk : Exceptions will disappear in the future?
On Thursday, 7 January 2021 at 18:12:18 UTC, H. S. Teoh wrote: If you're unfamiliar with the subject, I recommend reading a textbook on compiler construction. I already read one. Because every introduced catch block in the libunwind implementation introduces additional overhead. But only when an exception is thrown, right? Wrong. Out of memory only occurs at specific points in the code (i.e., when you call a memory allocation primitive). What about pushing a new stack frame on top/bottom of the stack? This is very implicit. I don't talk about a theoretical Turing machine with unbounded memory, rather about a linear bounded automaton with finite memory. What happens if stack memory isn't available anymore? As I said, I don't know how this is handled in D, but in theory you can even inline an already compiled function though you need meta information to do that. This tells me that you do not understand how compiled languages work. Traditionally, inlining means the insertion of code from the callee into the caller, yes. Imagine now, that the source code of the callee isn't available because it is already compiled and wrapped in a dynlib/static lib before (and now you link to that dynlib/static lib), then you can't inline the source code, but you can inline the binary code of the callee. For this to be "optimize-safe" regarding exceptions you need to store some meta information, e.g. the line number of all direct thrown exceptions in it, during the compilation of the callee in the dynlib/static lib for any caller outside the dynlib/static lib. Theoretically, you can even pass functions as binary code blocks to the callee, this is mostly inperformant, but it is at least possible. Though, I assume that most compiles doesn't any sort of this, but it doesn't mean that it isn't possible. Again, I recommend reading a textbook on compiler construction. It will help you understand this issues better. (And it will also indirectly help you write better code, once you understand what exactly the compiler does with it, and what the machine actually does.) It also depends on the considered compiler and how it is relating to the design discussed in textbooks. All of this information is already available at compile-time. The compiler can be easily emit code to write this information into some error-handling area that can be looked up by the catch block. Yes, but the line number is changing when inlining the code, and we don't want the new line number to be outputed by the runtime if an exception was thrown because it points to a line number only visible to the optimizer not to the user? Also, you are confusing debugging information with the mechanism of try/catch. So you only want to output line numbers in stack trace during debugging and not in production code?
Re: DConf talk : Exceptions will disappear in the future?
On Thursday, 7 January 2021 at 14:34:50 UTC, H. S. Teoh wrote: This has nothing to do with inlining. Inlining is done at compile-time, and the inlined function becomes part of the caller. True There is no stack pointer decrementing involved anymore Also true. because there's no longer a function call in the emitted code. And this is the problem, how to refer to the original line of the inlined function were the exception was thrown? We need either some machinery for that to be backpropagated or we didn't inline at all in the said case. One very important assumption is control flow: if you have operations A, B, C in your function and the optimizer can assume that control will always reach all 3 operations, then it can reorder the operations (e.g., to improve instruction cache coherence) without changing the meaning of the code. Wonderful, we have an example! If all three operations don't refer to depend on each other. Or maybe the compiler execute them in parallel. Did we refer to lazy evaluation or asynchronous code execution here? If the exception were propagated via normal return mechanisms, then the optimizer still has a way to optimize it: it can do A and C first, then if B fails it can insert code to undo C, which may still be faster than doing A and C separately. Puh, that's sounds a bit of reordering nondeterministic effectful operations which definitely aren't rollbackable in general, only in simple cases. But in general, why not generate a try catch mechanism at compile time catching the exception in case B throws and store it temporarily in an exception variable. After A has executed and was successful, just rethrow the exception of B. All this could be generated at compile time, no runtime cost but involves some kind of code duplication. This is why performance-conscious people prefer nothrow where possible: it lets the optimizer make more assumptions, and thereby, opens the possibility for better optimizations. But the assumption is wrong, every function can fail, e.g. out of memory, aborting the whole program in this case just to do better optimizations isn't the fine english way. This makes no sense. Inlining is done at compile-time; if you are loading the code as a dynamic library, by definition you're not inlining anymore. As I said, I don't know how this is handled in D, but in theory you can even inline an already compiled function though you need meta information to do that. My idea was just to fetch the line number from the metadata of the throw statement in the callee in order to localize the error correctly in the original source code.
Re: DConf talk : Exceptions will disappear in the future?
On Thursday, 7 January 2021 at 10:36:39 UTC, Jacob Carlborg wrote: Swift can throw anything that implements the Error protocol. Classes, structs and enums can implement protocols. True, Swift can throw anything what implements the Error protocol. It seems the error protocol itself doesn't define any constraints how an error has to look like. I'm contemplating if this is a good idea, maybe, I don't know yet. Some platforms implement C++ exception using longjmp, for example, iOS. Interesting, I've heard some OSes don't support exception tables, therefore an alternate implementation have to be chosen. It's claimed that exceptions are not zero cost, even when an exception is not thrown. Because the compiler cannot optimize functions that may throw as well as those that cannot throw. Did you refer to the case a pure function is inlined into the caller and the machinery of stack pointer decrementation doesn't work anymore? You may be right about that. However, I think it can be transformed safely in case the source code is still available. In case of dyn libs, we may, can develop a machinery to gather exception table information at compile time and to manipulate them in order to inline them safely, but I don't know about the case in D though.
Re: DConf talk : Exceptions will disappear in the future?
On Wednesday, 6 January 2021 at 21:27:59 UTC, H. S. Teoh wrote: It must be unique because different functions may return different sets of error codes. If these sets overlap, then once the error propagates up the call stack it becomes ambiguous which error it is. Contrived example: enum FuncAError { fileNotFound = 1, ioError = 2 } enum FuncBError { outOfMem = 1, networkError = 2 } int funcA() { throw FuncAError.fileNotFound; } int funcB() { throw FuncBError.outOfMem; } void main() { try { funcA(); funcB(); } catch (Error e) { // cannot distinguish between FuncAError and // FuncBError } } Thanks, reminds on swift error types which are enum cases. So the type is the pointer to the enum or something which describes the enum uniquely and the context is the enum value, or does the context describe where to find the enum value in the statically allocated object. Using the typeid is no good because: (1) typeid in D is a Sorry, I misspelled it, I meant the internal id in which type is turned to by the compiler, not the RTTI structure of a type at runtime. If you're in @nogc code, you can point to a statically-allocated block that the throwing code updates with relevant information about the error, e.g., a struct that contains further details about the error But the amount of information for an error can't be statically known. So we can't pre-allocate it via a statically allocated block, we need some kind of runtime polymorphism here to know all the fields considered. You don't need to box anything. The unique type ID already tells you what type the context is, whether it's integer or pointer and what the type of the latter is. The question is how can a type id as integer value do that, is there any mask to retrieve this kind of information from the type id field, e.g. the first three bits say something about the context data type or did we use some kind of log2n hashing of the typeid to retrieve that kind of information. When you `throw` something, this is what is returned from the function. To propagate it, you just return it, using the usual function return mechanisms. It's "zero-cost" because it the cost is exactly the same as normal returns from a function. Except that bit check after each call is required which is neglectable for some function calls, but it's summing up rapidly for the whole amount of modularization. Further, the space for the return value in the caller needs to be widened in some cases. Only if you want to use traditional dynamically-allocated exceptions. If you only need error codes, no polymorphism is needed. Checking the bit flag is runtime polymorphism, checking the type field against the catches is runtime polymorphism, checking what the typeid tells about the context type is runtime polymorphism. Checking the type of information behind the context pointer in case of non error codes is runtime polymorphism. The only difference is it is coded somewhat more low level and is a bit more compact than a class object. What if we use structs for exceptions where the first field is the type and the second field the string message pointer/or error code? The traditional implementation of stack unwinding bypasses normal function return mechanisms. It's basically a glorified longjmp() to the catch block, augmented with the automatic destruction of any objects that might need destruction on the way up the call stack. It depends. There are two ways I know, either jumping or decrementing the stack pointer and read out the information in the exception tables. Turns out, the latter is not quite so simple in practice. In order to properly destroy objects on the way up to the catch block, you need to store information about what to destroy somewhere. I can't imagine why this is different in your case, this is generally the problem of exception handling independent of the underlying mechanism. Once the pointer of the first landing pad is known, the control flow continues as known before until the next error is thrown. You also need to know where the catch blocks are so that you know where to land. Once you land, you need to know how to match the exception type to what the catch block expects, etc.. To implement this, every function needs to setup standard stack frames so that libunwind knows how to unwind the stack. Touché, that's better in case of error returns. It also requires exception tables, an LSDA (language-specific data area) for each function, personality functions, etc.. A whole bunch of heavy machinery just to get things to work properly. Why would you want to insert it into a list? The context field is a type-erased pointer-sized value. It may not even be a pointer. Good point, I don't know if
Re: DConf talk : Exceptions will disappear in the future?
Citing Herb Sutter: As noted in §1.1, preconditions, postconditions, and assertions are for identifying program bugs, they are never recoverable errors; violating them is always corruption, undefined behavior. Therefore they should never be reported via error reporting channels (regardless of whether exceptions, error codes, or another style is used). Instead, once we have contracts (expected in C++20), users should be taught to prefer expressing these as contracts, and we should consider using those also in the standard library. Oh men, did you ever hear of non-determinism? Why not just use compile time contracts and path dependent typing to solve those problems as well? Because perfectionism is our enemy in productive development. And terminating the whole program doesn't help either, exactly for this purpose we have error types or contexts, to know to which degree we are required to terminate and this should hold even for contracts.
Re: DConf talk : Exceptions will disappear in the future?
On Tuesday, 5 January 2021 at 21:46:46 UTC, H. S. Teoh wrote: 4) The universal error type contains two fields: a type field and a context field. a) The type field is an ID unique to every thrown exception -- uniqueness can be guaranteed by making this a pointer to some static global object that the compiler implicitly inserts per throw statement, so it will be unique even across shared libraries. The catch block can use this field to determine what the error was, or it can just call some standard function to turn this into a string message, print it and abort. Why it must be unique? Doesn't it suffice to return the typeid here? b) The context field contains exception-specific data that gives more information about the nature of the specific instance of the error that occurred, e.g., an integer value, or a pointer to a string description or block of additional information about the error (set by the thrower), or even a pointer to a dynamically-allocated exception object if the user wishes to use traditional polymorphic exceptions. Okay, but in 99% you need dynamically allocated objects because the context is most of the time simply unknown. But yes, in specific cases a simple error code suffice, but even then it would be better to be aware that an error code is returned instead of a runtime object. It sucks to me to box over the context pointer/value to find out if it is an error code or not when I only want an error code. c) The universal error type is constrained to have trivial move semantics, i.e., propagating it up the call stack is as simple as blitting the bytes over. (Any object(s) it points to need not be thus constrained, though.) The value semantics of the universal error type ensures that there is no overhead in propagating it up the call stack. The universality of the universal error type allows it to represent errors of any kind without needing runtime polymorphism, thus eliminating the overhead the current exception implementation incurs. So it seems the universal error type just tells me if there is or isn't error and checking for it is just a bitflip? The context field, however, still allows runtime polymorphism to be supported, should the user wish to. Which in most of the cases will be required. The addition of the universal error type to return value is automated by the compiler, and the user need not worry about it. The usual try/catch syntax can be built on top of it. Of course, this was proposed for C++, so a D implementation will probably be somewhat different. But the underlying thrust is: exceptions become value types by default, thus eliminating most of the overhead associated with the current exception implementation. I didn't know exactly how this is implemented in D, but class objects are passed as simple pointer and pointers are likewise value types. Using value types itself doesn't guarantee anything about performance, because the context field of an exception can be anything you need some kind of boxing involving runtime polymorphism anyway. Stack unwinding is replaced by normal function return mechanisms, which is much more optimizer-friendly. I heard that all the time, but why is that true? This also lets us support exceptions in @nogc code. Okay, this would be optionally great. However, if we insert the context pointer into a List we may get a problem of cyclicity. There is no need for a cascade of updates if you do it right. As I hinted at above, this enumeration does not have to be a literal enumeration from 0 to N; the only thing required is that it is unique *within the context of a running program*. A pointer to a static global suffices to serve such a role: it is guaranteed to be unique in the program's address space, and it fits in a size_t. The actual value may differ across different executions, but that's not a problem: any references to the ID from user code is resolved by the runtime dynamic linker -- as it already does for pointers to global objects. This also takes care of any shared libraries or dynamically loaded .so's or DLLs. What means unique, why is it important? Type ids aren't unique to distinguish exceptions and I don't know why we need this requirement. The point in Rust or Java was to limit the plurality of error types a function call receive, but this is exactly the point where idiomatic and productive development differs. Assumptions change and there you are. I've said this before, that the complaints about the current exception handling mechanism is really an issue of how it's implemented, rather than the concept of exceptions itself. Okay, I think this is definitely debatable. If we implement Sutter's proposal, or something similar suitably adapted to D, it would eliminate the runtime overhead, solve the @nogc exceptions issue, and still support traditional
Re: DConf talk : Exceptions will disappear in the future?
Personally, I don't appreciate error handling models much which pollute the return type of each function simply because of the conclusion that every function you define have to handle errors as errors can happen everywhere even in pure functions. You don't believe me? What about memory overflow errors which can occur in any stack/heap allocation? Don't know how this is handled in D but in Java you got exceptions for this as well. The other point is the direction which is chosen in Go and Rust to make error handling as deterministic as possible by enumerating all possible error types. Afaict, this isn't a good idea as this increases the fragile code problem by over specifying behavior. Any change requires a cascade of updates if this is possible at all. What you do in Rust then?, simply panic? Though, it doesn't mean that it is bad in every case. By churning different language design forums it all comes down to the point, every time, that the default error handling model equipped with the considered language is dump and that people call for extensions from other languages, even ones which include exception handling to improve things. I see this in Rust and Go and even in Swift forums that people are annoyed how it currently works. No error handling model was the HIT and will never be, therefore I would recommend to leave things as they are and to develop alternatives and not to replace existing ones.
Re: Recursively defined matcher function
On Sunday, 3 January 2021 at 18:55:58 UTC, sighoya wrote: ``` alias matcherSignature(T:matcherSignature!T) = Matcher (T[] matchers...); ``` Yet, it is right: ``` alias matcherSignature(T:matcherSignature!T) = Matcher function(T[] matchers...); ``` But it didn't work likewise, you have to instantiate it infinitely in nesting order. I think you either need some kind of nominalism to state matcherSignature!T exists for any T or you need to end it after a certain depth: ``` struct theEnd {} template matcherSignature(T) if (T==matcherSignature!T || T==theEnd) { Matcher function(T[] matchers...) fp; } ```
Re: Recursively defined matcher function
On Sunday, 3 January 2021 at 18:49:40 UTC, sighoya wrote: ``` alias matcherSignature(T:matcherSignature!T) = T (T[] matchers...); ``` Ahhh, I meant this: ``` alias matcherSignature(T:matcherSignature!T) = Matcher (T[] matchers...); `` I think we would require proper singleton types to model this as: ``` matcherSignature(matcherSignature.type[] matcherSignatures ...) ``` and this: ``` Matcher matcherSignature(matcherSignature.type[] matcherSignatures...) ```
Re: Recursively defined matcher function
On Sunday, 3 January 2021 at 18:26:44 UTC, Per Nordlöw wrote: I've tried alias Matcher = Match function(Matcher[] matchers...); but it errors as recursive alias declaration The closest thing can I think of is: ``` alias matcherSignature(T:matcherSignature!T) = T (T[] matchers...); ``` I think we would require proper singleton types to model this as: ``` matcherSignature(matcherSignature.type[] matcherSignatures ...) ```
Re: C++ or D?
On Wednesday, 30 December 2020 at 21:12:43 UTC, Ola Fosheim Grøstad wrote: (which does not work, but maybe there is some other way to express it?): See: https://forum.dlang.org/thread/ooxzbrmbrzpsefiro...@forum.dlang.org?page=1
Re: C++ or D?
On Wednesday, 30 December 2020 at 18:45:03 UTC, Ola Fosheim Grøstad wrote: Some people in the D community has for a long time wanted stack-less coroutines. This is now available in C++20, and maybe D can borrow the C++ implementation for LDC? That is an interesting possibility for sure. Hmm, I don't know much about them, but it seems they are a restriction to stackful coroutines, maybe a keyword addition like @stackless to prohibit nested suspension does the trick? The main addition for C++20 is concepts. Basically wrapping up ugly tests in a simple template parameter type specifier so that you can specify that a template parameter MUST support Addition or be a Stack or whatever you want to require with very simple syntax. (It also makes it easier to specify tests.) In my eyes, adding proper support for opImplicitCoercion enables the reuse of interfaces as typeclasses, yielding more potential for idiomatic development than utilizing C++ concepts alone. Btw, did D ever get to add nested template parameters? I know people asked for it a long time ago, but cannot remember if that was resolved somehow? Oh, what's this? Did you mean true Higher Kinded Types (HKT) ``` fun(R,S,T:R=>S) ``` That would be great, but they're simply arguing to use alias symbols instead. The downside with aliases is however a more restricted type inference: https://github.com/dlang/DIPs/blob/bf5157d3dc29a591826e22d188448fbc04ca81b2/DIPs/DIP1023.md https://forum.dlang.org/thread/dnyqxmgdazczwmmva...@forum.dlang.org?page=4 I almost never use multiple inheritance myself, but if you don't use it, it does not affect you at all? So how could it be a problem to have the option? *shrugs* It's right, at least theoretically, practically you are often forced to use the feature in order to utilize the functionality you need. But the more general point against this was the clash of fields and methods. However, this is akin to the problem with generic specialization, and likewise it can be solved by simply defining a new default instance, that's it.
Re: C++ or D?
On Wednesday, 30 December 2020 at 14:41:28 UTC, Ola Fosheim Grøstad wrote: Most of the statements are wrong too... "4732 features, but not a single one you actually want": wrong again, C++20 has features that people would like to see in D Could you elaborate a bit more, please? I'm interested. "unsatisfying standard library": not really, unless you need to avoid exceptions "template metaprogramming only capable of being understood by mensa member": some aspects of C++20 metaprogramming is easier than D That would me interest, too! "multiple-inheritance hell": no idea what this means, MI is usually used for the same as D interfaces Just to say, I think not including multiple inheritance was a mistake for Java and all its descendants, though not a big one. Multiple inheritance needs just a good convention, that's all.
Re: How to reliably detect an alias sequence?
On Sunday, 27 December 2020 at 12:23:26 UTC, Max Samukha wrote: Given a name bound to an alias sequence, what is a reliable way to detect that? import std.meta: AliasSeq; alias a = AliasSeq!int; static if () { } __traits(isSame, a, AliasSeq!a) could work but doesn't, because it flattens singletons, which means __traits(isSame, int, AliasSeq!int) is true. (A bug/bad design, IMO. If the intended behavior is not to flatten tuples passed to __traits, there is no reason to make a special case for singletons). This should work: ``` static assert(is(int == AliasSeq!int)); Drepl: static assert: `is(int == (int))` is false ``` The most appropriate solution would be: ``` AliasSeq!int.stringof == (int); ``` But it wouldn't exclude false positives.
Re: Get the code of any D-entity as string?
On Sunday, 27 December 2020 at 14:40:01 UTC, Basile B. wrote: A more concrete example of what you are trying to achieve would allow to show the D way. What happens with your code example for the struct: ``` struct S { ulong[] a; @E(0) const int b=1; void v() const {return;} int v(int i) const {return i+1;} } ``` Did I get the value/function body? Even if get that some day, what about function/value declarations in the free scope (module scope)? A realistic scenario would be mocking, you read in the declaration of the type to test and create mock variables for the variables referenced in the function body to decouple it from other modules.
Re: Get the code of any D-entity as string?
On Sunday, 27 December 2020 at 04:13:53 UTC, Max Haughton wrote: Not possible although implementing as a __trait would be about 15 lines I think. I think that too, and it would nicely reuse the work of the compiler to parse the whole project. I think read only AST access in some form would actually be quite nice, if not for dmd's AST being fuckugly (the hierarchy is fine but it's more gcc than clang) The point is, once we have that we can decide ourselves which framework is the best for meta modelling. Is it pure string manipulation or token stream manipulation à la Rust or an AST manipulation à la Nim/Scala. It can be all defined on top.
Re: Get the code of any D-entity as string?
On Saturday, 26 December 2020 at 21:45:41 UTC, Basile B. wrote: struct E {int a;} struct S { ulong[] a; @E(0) const int b; void v() const {} void v(int) const {} } string getDeclaration(T)() if (is(T == struct)) { import std.traits, std.meta; string result = "struct " ~ T.stringof ~ " {\n"; static foreach (m; __traits(allMembers, T)) {{ static if (isCallable!(__traits(getMember, T, m))) alias sym = __traits(getOverloads, T, m); else alias sym = AliasSeq!(__traits(getMember, T, m)); static foreach (s; sym) { result ~= ""; static foreach (a; __traits(getAttributes, s)) result ~= "@" ~ a.stringof ~ " "; static if (is(typeof(s))) result ~= typeof(s).stringof ~ " " ~ m ~ ";\n"; } }} result ~= "}"; return result; } pragma(msg, getDeclaration!S); --- Thanks a lot, retrieving type declarations as string is better than nothing. But it is still not enough, for instance retrieving the expressions of struct fields as string and also recursively resolving the expressions of the variables in the string expression as string. This is also a limitation in Nim macros, I think, you can't capture values outside the input scope.
Re: Get the code of any D-entity as string?
On Friday, 25 December 2020 at 23:04:15 UTC, Ali Çehreli wrote: I am probably misunderstanding it but there is the .stringof property for all types: T.stringof. But does stringof really print the declaration as string and not the type name itself? I found: https://dlang.org/spec/property.html#stringof I am probably stating the obvious but you normally import the interesting modules of that library anyway and the type information is available that way. Ali Well, certainly, yes. But only if `stringof` would do the job. The best thing would be to retrieve whole modules (abstract files) as string. On top of that we can retrieve any value/type declaration.
Get the code of any D-entity as string?
I've read a bit in Dlang traits. It now has the ability to retrieve all method signatures of an overload set. Big plus from me. Is generally possible to get the declaration of a type/module/value as string in traits? I didn't have any concrete use case for it, but it would essentially allow us to reflect over any code (we may have to respect privacy, of course). On top of that, people could write their own token or ast frameworks as library solutions. Further, most of the trait functionality given in the trait library could be simply implemented as a library solution. I don't know D enough to be sure it isn't yet possible. Theoretically, we could read all files in a source folder, but I want also to read declarations from types outside my source folders, e.g. from those located in dyn libs.
Re: C++ or D?
On Thursday, 24 December 2020 at 08:36:54 UTC, RSY wrote: You can use GC, you can disable it, you can use malloc, it suits all your need! Not to forget the availability of refcounted (shared) and unique ptr too in D.
Re: Why is creating of if Expressions not allowed?
On Sunday, 24 March 2019 at 18:59:45 UTC, Adam D. Ruppe wrote: Think of mixin() not as pasting code per se, but pasting an AST node inside the compiler's data structures. It must be a complete node of that tree that can be substituted in. And because AST Nodes aren't expressions means you can't pass them. This makes sense. I would really like to use normal mixin templates for these things, but why I can't insert only declarations with mixin templates? Sure, I can use q{} for better syntax highlighting in string mixins but I can't expand args inside them which I can however with mixin templates.
Re: Why is creating of if Expressions not allowed?
On Sunday, 24 March 2019 at 18:43:51 UTC, Paul Backus wrote: You can't return the result of a string mixin from a function. Instead, return a string from your `GenIf` function, and mix it in at the call site: string GenIf() { return "if (true) { return true; } else { return false; }"; } bool testFunction() { mixin(GenIf()); } Thanks for mentioning this, this was where I looking for.
Re: Why is creating of if Expressions not allowed?
On Sunday, 24 March 2019 at 16:57:25 UTC, Rémy Mouëza wrote: On Sunday, 24 March 2019 at 16:18:49 UTC, sighoya wrote: Why auto GenIf()() { return mixin("if(true) { return true;} else {return false;}"); } public bool testFunction2() { GenIf!(); } gives me: onlineapp.d-mixin-3(3): Error: expression expected, not if onlineapp.d(8): Error: template instance `onlineapp.GenIf!()` error instantiating Because D uses if statements, no if expressions. The equivalent of an if expression is the ternary operator: bool-condition ? if-true : if-false; Hmm..., sounds like bad news. Is there any mixin technology for statements?
Why is creating of if Expressions not allowed?
Why auto GenIf()() { return mixin("if(true) { return true;} else {return false;}"); } public bool testFunction2() { GenIf!(); } gives me: onlineapp.d-mixin-3(3): Error: expression expected, not if onlineapp.d(8): Error: template instance `onlineapp.GenIf!()` error instantiating
Re: Does D support nested Templates aka Higher Kinded Polymorphism?
On Tuesday, 3 October 2017 at 15:52:52 UTC, sighoya wrote: On Tuesday, 3 October 2017 at 15:30:52 UTC, Daniel Kozak wrote: writeln(bar!(Bar,Foo,int)(Bar!(Foo,int)())); Dne 3. 10. 2017 3:55 odpoledne napsal uživatel "sighoya via Digitalmars-d-learn" : But when I write this to: writeln(bar!(Bar,Foo,int)(Bar!(Foo,int))); it complains by: test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(17): Error: template instance test.bar!(Bar, Foo, int) error instantiating I still get test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(18): Error: template instance test.bar!(Bar, Foo, int) error instantiating by struct Foo(T) { T value; } struct Bar (R,S) { R!S fooint; } //T!S foo(S, alias T)(T!S v) { return v; } T!(S!R) bar(alias T,alias S,R)(T!(S!R) v) {return v;} void main() { import std.stdio; //writeln(foo!(int, Foo)(Foo!int(1))); //writeln(bar!(Bar,Foo,int)(Bar!(Foo,int))); writeln(bar!(Bar,Foo,int)(Bar!(Foo,int)())); } Thanks!!!
Re: Does D support nested Templates aka Higher Kinded Polymorphism?
On Tuesday, 3 October 2017 at 15:30:52 UTC, Daniel Kozak wrote: writeln(bar!(Bar,Foo,int)(Bar!(Foo,int)())); Dne 3. 10. 2017 3:55 odpoledne napsal uživatel "sighoya via Digitalmars-d-learn" : But when I write this to: writeln(bar!(Bar,Foo,int)(Bar!(Foo,int))); it complains by: test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(17): Error: template instance test.bar!(Bar, Foo, int) error instantiating I still get test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(18): Error: template instance test.bar!(Bar, Foo, int) error instantiating by struct Foo(T) { T value; } struct Bar (R,S) { R!S fooint; } //T!S foo(S, alias T)(T!S v) { return v; } T!(S!R) bar(alias T,alias S,R)(T!(S!R) v) {return v;} void main() { import std.stdio; //writeln(foo!(int, Foo)(Foo!int(1))); //writeln(bar!(Bar,Foo,int)(Bar!(Foo,int))); writeln(bar!(Bar,Foo,int)(Bar!(Foo,int)())); }
Re: Does D support nested Templates aka Higher Kinded Polymorphism?
But when I write this to: writeln(bar!(Bar,Foo,int)(Bar!(Foo,int))); it complains by: test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(11): Error: template instance T!(S!int) does not match template declaration Bar(R, S) test.d(17): Error: template instance test.bar!(Bar, Foo, int) error instantiating
Re: Does D support nested Templates aka Higher Kinded Polymorphism?
Thanks, How do I construct such a type. I don't get it done right with: struct Foo(T) { T value; } struct Bar (R,S) { R!S fooint; } T!(S!R) bar(alias T,alias S,R)(T!(S!R) v) {return v;} void main() { import std.stdio; writeln(bar!(Bar,Foo,int)(Bar!(Foo!int(1.0; }
Re: Does D support nested Templates aka Higher Kinded Polymorphism?
On Tuesday, 3 October 2017 at 12:09:04 UTC, rikki cattermole wrote: On 03/10/2017 1:05 PM, sighoya wrote: Especially, I mean something like T foo(S,T)(T i) { ... } struct Foo(T) { T value; } T!S foo(S, alias T)(T!S v) { return v; } void main() { import std.stdio; writeln(foo!(int, Foo)(Foo!int(1))); } Cool, but it seems that only a nesting of two is allowed, right? This one gives an error: T!S!R bar(alias T,alias S,R)(T!S!R v) {return v;} Error: multiple ! arguments are not allowed
Does D support nested Templates aka Higher Kinded Polymorphism?
Especially, I mean something like T foo(S,T)(T i) { ... }