Re: Example of Rust code
Am Sun, 12 Aug 2012 00:17:44 +0200 schrieb Timon Gehr timon.g...@gmx.ch: On 08/11/2012 01:24 PM, Marco Leise wrote: Am Fri, 10 Aug 2012 15:56:53 +0200 schrieb Timon Gehrtimon.g...@gmx.ch: int eval(scope Expr* e){ final switch(e.tag) with(Expr.Tag){ case val: return e.i; case plus: return eval(e.a) + eval(e.b); case minus: return eval(e.a) - eval(e.b); } } Can you quickly explain the use of scope here? Does that mean I wont keep a reference to e? It means I won't keep a reference to *e, but I assume that is what was meant. What are the implications? The caller has some confidence that passing a pointer to stack- allocated data is safe. Does scope change the method signature? Yes. It is eg. impossible to override a method that has a scope parameter with a method that does not have a scope parameter. Does the compiler enforce something? In this case and currently, it is merely documentation. I think it should be enforced and cast(scope) should be added to allow non-@safe code to escape the conservative analysis. Will generated code differ? Only the mangled symbol name will differ. (unlike when scope is used on delegate parameters, in this case it prevents closure allocation at the call site.) Does it prevent bugs or is it documentation for the user of the function? It is just documentation, both for the user and the maintainer. Thanks in advance for some insight! Now that looks like a good 'scope' FAQ. thx -- Marco
Re: Example of Rust code
Am 11.08.2012 20:37, schrieb Peter Alexander: On Saturday, 11 August 2012 at 18:04:29 UTC, Paulo Pinto wrote: On Saturday, 11 August 2012 at 16:12:14 UTC, Peter Alexander wrote: On Saturday, 11 August 2012 at 14:45:55 UTC, Russel Winder wrote: On Sat, 2012-08-11 at 02:19 +0200, David Piepgrass wrote: […] I hope someday to have a programming system whose features are not limited to whatever features the language designers saw fit to include -- a language where the users can add their own features, all the while maintaining native efficiency like D. That language would potentially allow Rust-like code, D-like code, Ruby-like code and even ugly C-like code. I guess you don't want to be the one to kickstart that PL. I've been planning to do it myself, but so far the task seems just too big for one person. quasi-troll Isn't that language Lisp? /quasi-troll You missed the native efficiency part :-) You mean like the Common Lisp compilers that are able to beat FORTRAN compilers in floating point computations? http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.54.5725 -- Paulo Not sure where you read that in the paper. From the conclusion: We have demonstrated that the speed of compiled Common Lisp code, though today somewhat slower than that of the best compiled Fortran, could probably be as efficient, and in some ways superior. Probably is the operative word there. Should have re-read the paper. It has been a few years since I fully read it. Still I think Common Lisp code is pretty efficient. Most modern Lisp implementations employ JITing one way or another, so you do get native code. Just not on the first run through a bit of code. JIT has its limits. A dynamically typed language is still dynamically typed once compiled. Sure the JIT may be able to deduce the types in some cases, but not all. I do see your point, but in general it's still not as fast as optimised C. I imagine you wanted to answer to Russel's post. On Lisp's case, most systems available today only JIT when using the REPL, as you can always compile to native code. Type annotations help improve the speed in hotspot areas of your code, in case you really need the extra speed for the application use case. As for the speed of native code produced by JITs for dynamic languages, I think Cog(Smalltalk), Self(Smaltalk ended up becoming JVM Hotspot), PyPy (Python), V8(JavaScript), LLVM(Julia) prove that you can get pretty close to C for the majority of use cases that matter to the common user. Actually a running assumption among the dynamic languages advocates is that if dynamic languages had had as much money and research support as the static languages field, the compilers would be much better by now. -- Paulo
Re: Example of Rust code
Am Sun, 12 Aug 2012 00:17:44 +0200 schrieb Timon Gehr timon.g...@gmx.ch: On 08/11/2012 01:24 PM, Marco Leise wrote: Am Fri, 10 Aug 2012 15:56:53 +0200 schrieb Timon Gehrtimon.g...@gmx.ch: int eval(scope Expr* e){ final switch(e.tag) with(Expr.Tag){ case val: return e.i; case plus: return eval(e.a) + eval(e.b); case minus: return eval(e.a) - eval(e.b); } } Can you quickly explain the use of scope here? Does that mean I wont keep a reference to e? It means I won't keep a reference to *e, but I assume that is what was meant. What are the implications? The caller has some confidence that passing a pointer to stack- allocated data is safe. Does scope change the method signature? Yes. It is eg. impossible to override a method that has a scope parameter with a method that does not have a scope parameter. Does the compiler enforce something? In this case and currently, it is merely documentation. I think it should be enforced and cast(scope) should be added to allow non-@safe code to escape the conservative analysis. There are probably some more, less known use cases. For example this recent thread on stackoverflow shows how scope might be necessary to initialize an immutable variable. http://stackoverflow.com/questions/11860584/changing-immutable-members-inside-the-constructor
Re: Example of Rust code
Paulo Pinto: As for the speed of native code produced by JITs for dynamic languages, I think Cog(Smalltalk), Self(Smaltalk ended up becoming JVM Hotspot), PyPy (Python), V8(JavaScript), LLVM(Julia) prove that you can get pretty close to C for the majority of use cases that matter to the common user. Among V8 developers thee are some ex Self developers. I think at the moment there aren't enough Julia benchmarks to allow us to judge its performance well enough. PyPy is surely not close to C speeds when it JITs Python code (PyPy developers need to stop using just their few benchmarks and try to optimize many other programs). And you miss the best of the bunch, the Lua JIT. One problem with JITs is that they give you a good performance if they are well implemented and if the the GC is good. While older languages produce decent performance even with a simple compiler. Bye, bearophile
Re: Example of Rust code
On Sunday, 12 August 2012 at 11:28:28 UTC, bearophile wrote: Paulo Pinto: As for the speed of native code produced by JITs for dynamic languages, I think Cog(Smalltalk), Self(Smaltalk ended up becoming JVM Hotspot), PyPy (Python), V8(JavaScript), LLVM(Julia) prove that you can get pretty close to C for the majority of use cases that matter to the common user. Among V8 developers thee are some ex Self developers. I think at the moment there aren't enough Julia benchmarks to allow us to judge its performance well enough. PyPy is surely not close to C speeds when it JITs Python code (PyPy developers need to stop using just their few benchmarks and try to optimize many other programs). And you miss the best of the bunch, the Lua JIT. Yeah, silly me forgeting about LuaJIT. One problem with JITs is that they give you a good performance if they are well implemented and if the the GC is good. While older languages produce decent performance even with a simple compiler. Bye, bearophile True, this year's Google IO V8 talk has lots of informations on how you write JavaScript code can influence the code quality generated by V8. From the compiler design geek I used to be, I find very interesting to get myself informed about this area, and I think that dynamic languages JITs still have a lot of room to improve. Although personally I prefer static languages with native compilers for my own coding projects. -- Paulo
Re: Example of Rust code
On 08/12/2012 10:17 AM, Johannes Pfau wrote: There are probably some more, less known use cases. For example this recent thread on stackoverflow shows how scope might be necessary to initialize an immutable variable. http://stackoverflow.com/questions/11860584/changing-immutable-members-inside-the-constructor Such rules are not part of the current language. The comment that says that they are is wrong. scope does not currently influence type checking at method call boundaries.
Re: Example of Rust code
On Saturday, 11 August 2012 at 11:24:35 UTC, Marco Leise wrote: Can you quickly explain the use of scope here? Does that mean I wont keep a reference to e? What are the implications? Does scope change the method signature? Does the compiler enforce something? Will generated code differ? Does it prevent bugs or is it documentation for the user of the function? Thanks in advance for some insight! The generated code is different when the parameter is a delegate (no closure is allocated in cases of anonymous functions/lamdas or expressions like myNestedFunction). It's supposed to be enforced by the compiler that no references escape, but currently it's just documentation beyond the case of delegates.
Re: Example of Rust code
On Sat, 2012-08-11 at 02:19 +0200, David Piepgrass wrote: […] I hope someday to have a programming system whose features are not limited to whatever features the language designers saw fit to include -- a language where the users can add their own features, all the while maintaining native efficiency like D. That language would potentially allow Rust-like code, D-like code, Ruby-like code and even ugly C-like code. I guess you don't want to be the one to kickstart that PL. I've been planning to do it myself, but so far the task seems just too big for one person. quasi-troll Isn't that language Lisp? /quasi-troll -- Russel. = Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.win...@ekiga.net 41 Buckmaster Roadm: +44 7770 465 077 xmpp: rus...@winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder signature.asc Description: This is a digitally signed message part
Re: Example of Rust code
Am Fri, 10 Aug 2012 15:56:53 +0200 schrieb Timon Gehr timon.g...@gmx.ch: int eval(scope Expr* e){ final switch(e.tag) with(Expr.Tag){ case val: return e.i; case plus: return eval(e.a) + eval(e.b); case minus: return eval(e.a) - eval(e.b); } } Can you quickly explain the use of scope here? Does that mean I wont keep a reference to e? What are the implications? Does scope change the method signature? Does the compiler enforce something? Will generated code differ? Does it prevent bugs or is it documentation for the user of the function? Thanks in advance for some insight! -- Marco
Re: Example of Rust code
On Sat, 11 Aug 2012 13:24:12 +0200, Marco Leise marco.le...@gmx.de wrote: Am Fri, 10 Aug 2012 15:56:53 +0200 schrieb Timon Gehr timon.g...@gmx.ch: int eval(scope Expr* e){ final switch(e.tag) with(Expr.Tag){ case val: return e.i; case plus: return eval(e.a) + eval(e.b); case minus: return eval(e.a) - eval(e.b); } } Can you quickly explain the use of scope here? Does that mean I wont keep a reference to e? What are the implications? Does scope change the method signature? Does the compiler enforce something? Will generated code differ? Does it prevent bugs or is it documentation for the user of the function? Thanks in advance for some insight! If I'm not mistaken, scope will enforce that the reference never escapes the function. So you cannot pass it to other functions that might keep it's reference or store it in any way.
Re: Example of Rust code
On Saturday, 11 August 2012 at 11:47:43 UTC, simendsjo wrote: If I'm not mistaken, scope will enforce that the reference never escapes the function. So you cannot pass it to other functions that might keep it's reference or store it in any way. It _should_ enforce that, but its implementation is lacking at this point. David
Re: Example of Rust code
On Sat, Aug 11, 2012 at 2:19 AM, David Piepgrass qwertie...@gmail.com wrote: I must say though, that while ADTs are useful for simple ASTs, I am not convinced that they scale to big and complex ASTs, let alone extensible ASTs, which I care about more. You mean AST for D code? Nevertheless ADTs are at least useful for rapid prototyping, and pattern matching is really nice too. I'm sure somebody could at least write a D mixin for ADTs, if not pattern matching.) I did it, maybe 2 years ago. I worked for recursive ADT too (lists, trees) and automatically generated small matchers, and maybe specific map/reduce. That would be easier today, with this CTFE++ we now have. IIRC, it generated an abstract class with internal subtypes and a tag to distinguish the state. I guess I could have used a union for the fields, like Timon did further upthread. Hmm, does a union allow for recursive fields? I never tried to do generic pattern matchers, that would work also on any struct and class, by using .tupleof. I daydreamed about it a few times, tough, but never found a palatable syntax. Maybe with the new () = syntax, that'd be better. I hope someday to have a programming system whose features are not limited to whatever features the language designers saw fit to include -- a language where the users can add their own features, all the while maintaining native efficiency like D. That language would potentially allow Rust-like code, D-like code, Ruby-like code and even ugly C-like code. I guess you don't want to be the one to kickstart that PL. I've been planning to do it myself, but so far the task seems just too big for one person. Well, we are not far from having an official D lexer. Then, an official D parser. From this, adding user-defined extensions is not *that* complicated (not simple, mind you, but doable). * define lowerings (aka, translations from your extended syntax to D syntax), maybe by snatching the unused macro keyword * code a small wrapper around dmd, rdmd-like: given a file, it extracts the macros, parses the extended code, transforms the extensions, does that as many times as necessary, if some macros call other macros. * Discard the macros and then pass the transformed file to dmd. It looks like C macros and preprocessor-based programming, but since it knows the D grammar, it's nearer Lisp macros, I think.
Re: Example of Rust code
On 08/10/12 14:32, bearophile wrote: (Repost from D.learn.) Through Reddit I've found a page that shows a small example of Rust code: http://www.reddit.com/r/programming/comments/xyfqg/playing_with_rust/ https://gist.github.com/3299083 The code: https://gist.github.com/3307450 - So I've tried to translate this first part of the Rust code to D (I have not run it, but it looks correct): enum expr { val(int), plus(expr, expr), minus(expr, expr) } fn eval(e: expr) - int { alt *e { val(i) = i, plus(a, b) = eval(a) + eval(b), minus(a, b) = eval(a) - eval(b) } } fn main() { let x = eval( minus(val(5), plus(val(3), val(1; io::println(#fmt(val: %i, x)); } Ugh. Haven't really read that article, but how about this D version: import std.stdio; template ALIAS(alias A) { alias A ALIAS; } static struct Expr(string EVAL, A...) { A a; static if (is(typeof(a[0].eval))) @property a0() { return a[0].eval; } else alias ALIAS!(a[0]) a0; static if (is(typeof(a[1]))) { static if (is(typeof(a[1].eval))) @property a1() { return a[1].eval; } else alias ALIAS!(a[1]) a1; } @property auto eval() { static if (is(typeof(mixin(EVAL return mixin(EVAL); else mixin(EVAL); } //alias eval this; // Uncommenting this line will enable automatic // evaluation -- which may not always be desirable. auto opBinary(string op, B)(B b) { return Expr!(a0 ~ op ~ a1, Expr, B)(this, b); } } auto Val(V)(V v) { return Expr!(a0, V)(v); } void main() { auto r = Val(5) - (Val(3) + Val(1)); writeln(r: , r, == , r.eval); auto s = sqr(Val(5) * Val(2) ^^ Val(3)); writeln(s: , s, == , s.eval); } auto sqr(T)(T a) { return Expr!(a0*a0, T)(a); } which is more readable while being much more powerful. But still trivial enough that the compiler (GDC) evaluates it all at compile time, even without being asked to do so. artur
Re: Example of Rust code
On Saturday, 11 August 2012 at 14:45:55 UTC, Russel Winder wrote: On Sat, 2012-08-11 at 02:19 +0200, David Piepgrass wrote: […] I hope someday to have a programming system whose features are not limited to whatever features the language designers saw fit to include -- a language where the users can add their own features, all the while maintaining native efficiency like D. That language would potentially allow Rust-like code, D-like code, Ruby-like code and even ugly C-like code. I guess you don't want to be the one to kickstart that PL. I've been planning to do it myself, but so far the task seems just too big for one person. quasi-troll Isn't that language Lisp? /quasi-troll You missed the native efficiency part :-) I think XL is the closest thing that currently exists. http://en.wikipedia.org/wiki/XL_(programming_language)
Re: Example of Rust code
On Saturday, 11 August 2012 at 16:12:14 UTC, Peter Alexander wrote: On Saturday, 11 August 2012 at 14:45:55 UTC, Russel Winder wrote: On Sat, 2012-08-11 at 02:19 +0200, David Piepgrass wrote: […] I hope someday to have a programming system whose features are not limited to whatever features the language designers saw fit to include -- a language where the users can add their own features, all the while maintaining native efficiency like D. That language would potentially allow Rust-like code, D-like code, Ruby-like code and even ugly C-like code. I guess you don't want to be the one to kickstart that PL. I've been planning to do it myself, but so far the task seems just too big for one person. quasi-troll Isn't that language Lisp? /quasi-troll You missed the native efficiency part :-) You mean like the Common Lisp compilers that are able to beat FORTRAN compilers in floating point computations? http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.54.5725 -- Paulo
Re: Example of Rust code
On Sat, 2012-08-11 at 18:12 +0200, Peter Alexander wrote: On Saturday, 11 August 2012 at 14:45:55 UTC, Russel Winder wrote: […] quasi-troll Isn't that language Lisp? /quasi-troll You missed the native efficiency part :-) Most modern Lisp implementations employ JITing one way or another, so you do get native code. Just not on the first run through a bit of code. […] -- Russel. = Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.win...@ekiga.net 41 Buckmaster Roadm: +44 7770 465 077 xmpp: rus...@winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder signature.asc Description: This is a digitally signed message part
Re: Example of Rust code
On Saturday, 11 August 2012 at 18:04:29 UTC, Paulo Pinto wrote: On Saturday, 11 August 2012 at 16:12:14 UTC, Peter Alexander wrote: On Saturday, 11 August 2012 at 14:45:55 UTC, Russel Winder wrote: On Sat, 2012-08-11 at 02:19 +0200, David Piepgrass wrote: […] I hope someday to have a programming system whose features are not limited to whatever features the language designers saw fit to include -- a language where the users can add their own features, all the while maintaining native efficiency like D. That language would potentially allow Rust-like code, D-like code, Ruby-like code and even ugly C-like code. I guess you don't want to be the one to kickstart that PL. I've been planning to do it myself, but so far the task seems just too big for one person. quasi-troll Isn't that language Lisp? /quasi-troll You missed the native efficiency part :-) You mean like the Common Lisp compilers that are able to beat FORTRAN compilers in floating point computations? http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.54.5725 -- Paulo Not sure where you read that in the paper. From the conclusion: We have demonstrated that the speed of compiled Common Lisp code, though today somewhat slower than that of the best compiled Fortran, could probably be as efficient, and in some ways superior. Probably is the operative word there. Most modern Lisp implementations employ JITing one way or another, so you do get native code. Just not on the first run through a bit of code. JIT has its limits. A dynamically typed language is still dynamically typed once compiled. Sure the JIT may be able to deduce the types in some cases, but not all. I do see your point, but in general it's still not as fast as optimised C.
Re: Example of Rust code
On 8/11/2012 11:04 AM, Paulo Pinto wrote: On Saturday, 11 August 2012 at 16:12:14 UTC, Peter Alexander wrote: You missed the native efficiency part :-) You mean like the Common Lisp compilers that are able to beat FORTRAN compilers in floating point computations? Floating point code is a rather specialized subset of what a good native compiler can do. For example, with Java, doing well with floating point has no relevance to the lack of user defined value types in Java, and the lack of efficiency that entails.
Re: Example of Rust code
On 08/11/2012 01:24 PM, Marco Leise wrote: Am Fri, 10 Aug 2012 15:56:53 +0200 schrieb Timon Gehrtimon.g...@gmx.ch: int eval(scope Expr* e){ final switch(e.tag) with(Expr.Tag){ case val: return e.i; case plus: return eval(e.a) + eval(e.b); case minus: return eval(e.a) - eval(e.b); } } Can you quickly explain the use of scope here? Does that mean I wont keep a reference to e? It means I won't keep a reference to *e, but I assume that is what was meant. What are the implications? The caller has some confidence that passing a pointer to stack- allocated data is safe. Does scope change the method signature? Yes. It is eg. impossible to override a method that has a scope parameter with a method that does not have a scope parameter. Does the compiler enforce something? In this case and currently, it is merely documentation. I think it should be enforced and cast(scope) should be added to allow non-@safe code to escape the conservative analysis. Will generated code differ? Only the mangled symbol name will differ. (unlike when scope is used on delegate parameters, in this case it prevents closure allocation at the call site.) Does it prevent bugs or is it documentation for the user of the function? It is just documentation, both for the user and the maintainer. Thanks in advance for some insight!
Re: Example of Rust code
On Saturday, 11 August 2012 at 22:17:44 UTC, Timon Gehr wrote: Will generated code differ? Only the mangled symbol name will differ. (unlike when scope is used on delegate parameters, in this case it prevents closure allocation at the call site.) The code for callee stays the same, yes, but the code for the caller might change as the optimizer is free to take advantage of the fact that any reference in the parameters will not be escaped by the function. For example, LDC will stack-allocate dynamic arrays and objects if they are local to the function. [1] David [1] The fine print: We currently don't take advantage of scope parameters for this yet, though (it seems too dangerous with the related analysis not being implemented in the frontend), and for a completely unrelated reason, the code which performs the mentioned optimization is disabled in current master (but will be re-enabled in the near future, before the September release).
Re: Example of Rust code
On 08/12/2012 12:34 AM, David Nadlinger wrote: On Saturday, 11 August 2012 at 22:17:44 UTC, Timon Gehr wrote: Will generated code differ? Only the mangled symbol name will differ. (unlike when scope is used on delegate parameters, in this case it prevents closure allocation at the call site.) The code for callee stays the same, yes, but the code for the caller might change as the optimizer is free to take advantage of the fact that any reference in the parameters will not be escaped by the function. For example, LDC will stack-allocate dynamic arrays and objects if they are local to the function. [1] David [1] The fine print: We currently don't take advantage of scope parameters for this yet, though (it seems too dangerous with the related analysis not being implemented in the frontend), and for a completely unrelated reason, the code which performs the mentioned optimization is disabled in current master (but will be re-enabled in the near future, before the September release). Is there an upper bound on the amount of allocated memory? Implicit stack-allocation of arbitrarily-sized dynamic arrays seems dangerous.
Example of Rust code
(Repost from D.learn.) Through Reddit I've found a page that shows a small example of Rust code: http://www.reddit.com/r/programming/comments/xyfqg/playing_with_rust/ https://gist.github.com/3299083 The code: https://gist.github.com/3307450 - So I've tried to translate this first part of the Rust code to D (I have not run it, but it looks correct): enum expr { val(int), plus(expr, expr), minus(expr, expr) } fn eval(e: expr) - int { alt *e { val(i) = i, plus(a, b) = eval(a) + eval(b), minus(a, b) = eval(a) - eval(b) } } fn main() { let x = eval( minus(val(5), plus(val(3), val(1; io::println(#fmt(val: %i, x)); } A comment from the little article: putting a in front of a expression allocates it on the stack and gives you a reference to it. so the lifetime of this tree is to the end of the run [main] function. - The first D version is easy enough to write, but: - It uses classes, I think each class instance uses more memory than what's used in the original Rust code. - All the class instances here are allocated on the Heap. This is less efficient than the Rust code, where all the data is stack-allocated. - This code contains boilerplate, it's long. - Writing eval() is easy, but in the first version of eval() there were two bugs. - The assert(0) in eval() is not nice. There is no compile-time safety. - The several dynamic casts in eval() are slow. interface Expr {} class Val : Expr { const int v; this(in int v_) pure nothrow { this.v = v_; } } class Plus : Expr { const Expr x, y; this(in Expr x_, in Expr y_) pure nothrow { this.x = x_; this.y = y_; } } class Minus : Expr { const Expr x, y; this(in Expr x_, in Expr y_) pure nothrow { this.x = x_; this.y = y_; } } int eval(in Expr e) pure nothrow { if (Val ve = cast(Val)e) return ve.v; else if (Plus pe = cast(Plus)e) return eval(pe.x) + eval(pe.y); else if (Minus me = cast(Minus)e) return eval(me.x) - eval(me.y); else assert(0); } void main() { auto ex = new Minus(new Val(5), new Plus(new Val(3), new Val(1))); import std.stdio; writeln(Val: , eval(ex)); } - This second D version uses the same class definitions, but allocates the class instances on the stack. The code is bug prone and ugly. The other disadvantages are unchanged: void main() { import std.stdio; import std.conv: emplace; import core.stdc.stdlib: alloca; enum size_t size_Val = __traits(classInstanceSize, Val); enum size_t size_Plus = __traits(classInstanceSize, Plus); enum size_t size_Minus = __traits(classInstanceSize, Minus); Val e1 = emplace!Val(alloca(size_Val)[0 .. size_Val], 5); Val e2 = emplace!Val(alloca(size_Val)[0 .. size_Val], 3); Val e3 = emplace!Val(alloca(size_Val)[0 .. size_Val], 1); Plus e4 = emplace!Plus(alloca(size_Plus)[0 .. size_Plus], e2, e3); Minus ex2 = emplace!Minus(alloca(size_Minus)[0 .. size_Minus], e1, e4); writeln(Val: , eval(ex2)); } - A third D version, using tagged structs: - It doesn't look nice, and it's long. - Class references can be null, so I have added tests at runtime in the pre-conditions. In the Rust code the references can't be null. - The structs are stack-allocated but the main() code is not nice. - The tags can't const or immutable, otherwise the compiler doesn't read the actual value of the various tags, assuming it's always Tag.none. - Too many casts make this code bug-prone. import std.stdio; enum Tag { none, val, plus, minus } struct Expr { Tag tag = Tag.none; } struct Val { Tag tag = Tag.val; immutable int v; this(int v_) pure nothrow { this.v = v_; } } struct Plus { Tag tag = Tag.plus; const Expr* x, y; this(in Expr* x_, in Expr* y_) pure nothrow in { assert(x_ != null); assert(y_ != null); } body { this.x = x_; this.y = y_; } } struct Minus { Tag tag = Tag.minus; const Expr* x, y; this(in Expr* x_, in Expr* y_) pure nothrow in { assert(x_ != null); assert(y_ != null); } body { this.x = x_; this.y = y_; } } int eval(in Expr* e) pure nothrow in { assert(e); } body { final switch (e.tag) { case Tag.none: assert(0); case Tag.val: return (cast(Val*)e).v; case Tag.plus: auto pe = cast(Plus*)e; return eval(pe.x) + eval(pe.y); case Tag.minus: auto me = cast(Minus*)e; return eval(me.x) - eval(me.y); } } void main() { const e1 = Val(5); const e2 = Val(3); const e3 = Val(1); const e4 = Plus(cast(Expr
Re: Example of Rust code
On Friday, 10 August 2012 at 12:32:28 UTC, bearophile wrote: This second D version uses the same class definitions, but allocates the class instances on the stack. The code is bug prone and ugly. The other disadvantages are unchanged: void main() { import std.stdio; import std.conv: emplace; import core.stdc.stdlib: alloca; enum size_t size_Val = __traits(classInstanceSize, Val); enum size_t size_Plus = __traits(classInstanceSize, Plus); enum size_t size_Minus = __traits(classInstanceSize, Minus); Val e1 = emplace!Val(alloca(size_Val)[0 .. size_Val], 5); Val e2 = emplace!Val(alloca(size_Val)[0 .. size_Val], 3); Val e3 = emplace!Val(alloca(size_Val)[0 .. size_Val], 1); Plus e4 = emplace!Plus(alloca(size_Plus)[0 .. size_Plus], e2, e3); Minus ex2 = emplace!Minus(alloca(size_Minus)[0 .. size_Minus], e1, e4); writeln(Val: , eval(ex2)); } Probably there are ways to improve my D versions, or to write better versions. Bye, bearophile I think version 2 would be the easiest one to improve, by including a combined emplace/alloca convenience function in Phobos for this common use-case. See the technique used in: http://www.digitalmars.com/d/archives/digitalmars/D/run-time_stack-based_allocation_166305.html auto Create(void* buf=alloca(frame_size))
Re: Example of Rust code
One simple possibility is import std.stdio; struct Expr{ enum Tag { val, plus, minus } union{int i;struct{Expr* a, b;}} Tag tag; } Expr val(int i){ Expr e;e.tag=Expr.Tag.val;e.i=i;return e;} Expr plus(Expr* a, Expr* b){Expr e;e.tag=Expr.Tag.plus; e.a=a; e.b=b;return e;} Expr minus(Expr* a, Expr* b){Expr e;e.tag=Expr.Tag.minus; e.a=a; e.b=b;return e;} int eval(scope Expr* e){ final switch(e.tag) with(Expr.Tag){ case val: return e.i; case plus: return eval(e.a) + eval(e.b); case minus: return eval(e.a) - eval(e.b); } } void main(){ auto five = val(5), three = val(3), one = val(1); auto add = plus(three, one); auto sub = minus(five, add); auto x= eval(sub); writeln(val: ,x); } It would of course be better if D supported ADTs.
Re: Example of Rust code
On 8/10/2012 5:32 AM, bearophile wrote: Through Reddit I've found a page that shows a small example of Rust code: Here's the D version: - import std.stdio; struct expr { int val; int eval() { return val; } } expr plus (expr a, expr b) { return expr(a.val + b.val); } expr minus(expr a, expr b) { return expr(a.val - b.val); } void main() { auto x = minus(expr(5), plus(expr(3), expr(1))).eval(); writeln(val: , x); } -- And the generated code: -- __Dmain comdat assume CS:__Dmain L0: pushEAX mov EAX,offset FLAT:_D3std5stdio6stdoutS3std5stdio4File pushdword ptr FLAT:_DATA[0Ch] pushdword ptr FLAT:_DATA[08h] push1 push0Ah callnear ptr _D3std5stdio4File18__T5writeTAyaTiTaZ5writeMFAyaiaZv xor EAX,EAX pop ECX ret I'd say we're doing all right.
Re: Example of Rust code
Walter Bright: I'd say we're doing all right. Are you serious? Bye, bearophile
Re: Example of Rust code
Tove: I think version 2 would be the easiest one to improve, by including a combined emplace/alloca convenience function in Phobos for this common use-case. See the technique used in: http://www.digitalmars.com/d/archives/digitalmars/D/run-time_stack-based_allocation_166305.html auto Create(void* buf=alloca(frame_size)) I see, thank you for the suggestion, seems interesting. And thank you to Timon Gehr for his compacted code. Walter's code seems to miss the point, but maybe he's trying to tell me something about very small demo programs. Bye, bearophile
Re: Example of Rust code
On 8/10/2012 3:42 PM, bearophile wrote: Walter Bright: I'd say we're doing all right. Are you serious? Yes. What's wrong with my D version? It's short and to the point, works, and produces optimal code.
Re: Example of Rust code
On 8/10/2012 3:46 PM, bearophile wrote: Walter's code seems to miss the point, but maybe he's trying to tell me something about very small demo programs. If you want something allocated on the stack, us a struct, not a class. It's what structs are for. You can also use templates with overloading to get stack allocated parametric polymorphism and zero runtime overhead. What I mean is: 1. If you write FORTRAN code in D, it will not work as well as writing FORTRAN in FORTRAN. 2. If you write C code in D, it will not work as well as writing C in C. 3. If you write Rust code in D, it will not work as well as writing Rust in Rust. If you want D code to perform, you gotta write it in D. Not in Rust, C, or Java.
Re: Example of Rust code
On 8/10/2012 4:19 PM, Walter Bright wrote: You can also use templates with overloading to get stack allocated parametric polymorphism and zero runtime overhead. It appears that Rust does not have function overloading. Is this correct?
Re: Example of Rust code
On 8/10/2012 5:02 PM, José Armando García Sancio wrote: On Fri, Aug 10, 2012 at 4:35 PM, Walter Bright It appears that Rust does not have function overloading. Is this correct? That is correct. Well, the type class thing looks like a lame substitute. Sorry.
Re: Example of Rust code
I'd say we're doing all right. Are you serious? Yes. What's wrong with my D version? It's short and to the point, works, and produces optimal code. Your version is basically a very long-winded way to say auto x = 5 - (3 + 1); so it really has nothing to do with the example. The point of the example was to represent a simple AST and store it on the stack, not to represent + and - operators as plus() and minus() functions. (I must say though, that while ADTs are useful for simple ASTs, I am not convinced that they scale to big and complex ASTs, let alone extensible ASTs, which I care about more. Nevertheless ADTs are at least useful for rapid prototyping, and pattern matching is really nice too. I'm sure somebody could at least write a D mixin for ADTs, if not pattern matching.) 1. If you write FORTRAN code in D, it will not work as well as writing FORTRAN in FORTRAN. 2. If you write C code in D, it will not work as well as writing C in C. Really? And here I genuinely thought D was good enough for all the things C and FORTRAN are used for. 3. If you write Rust code in D, it will not work as well as writing Rust in Rust. I hope someday to have a programming system whose features are not limited to whatever features the language designers saw fit to include -- a language where the users can add their own features, all the while maintaining native efficiency like D. That language would potentially allow Rust-like code, D-like code, Ruby-like code and even ugly C-like code. I guess you don't want to be the one to kickstart that PL. I've been planning to do it myself, but so far the task seems just too big for one person.
Re: Example of Rust code
Walter Bright: Thank you for the answer. 3. If you write Rust code in D, it will not work as well as writing Rust in Rust. If you want D code to perform, you gotta write it in D. Not in Rust, C, or Java. I agree. Every language has its strengths and its specific qualities, so you can't ask for a perfect translation from code in language X to language Y. On the other hand when X and Y languages are meant to be used for similar computing tasks, it's good to have some ways to translate the purposes of X code well enough to Y. Regarding the problem David Piepgrass has explained what was the point of that Rust code, ans why your code was missing the point. The main point of my little comparison was to show the usefulness of some Rust features that were discussed for D too, like pattern matching, tagged recursive structures (in Phobos there is std.variant.Algebraic, but it's currently not usable to write that code), and the original nice way of allocating a struct on the stack and return a reference to it, to build that expression tree. I suggest to welcome future comparisons between D with Rust in this D newsgroup, because full ignorance of Rust will _not_ help D growth. Bye, bearophile
Re: Example of Rust code
On 8/10/2012 3:42 PM, bearophile wrote: Walter Bright: I'd say we're doing all right. Are you serious? You can also do things called expression templates in D: import std.stdio; auto val(T)(T v) { static struct S { T v; int eval() { return v; }} auto s = S(v); return s; } auto plus(A,B)(A a, B b) { static struct S { A a; B b; int eval() { return a.eval() + b.eval(); }} auto s = S(a,b); return s; } auto minus(A,B)(A a, B b) { static struct S { A a; B b; int eval() { return a.eval() - b.eval(); }} auto s = S(a,b); return s; } void main() { auto x = minus(val(5), plus(val(3), val(1))); writeln(val: , x.eval()); } relying on parametric polymorphism. You could reduce the repetitive boilerplate by using a template mixin, but I just banged this out in a few minutes, and it illustrates the idea. Note that there is no heap allocation anywhere, nor even any testing/branching. The compiler should inline this, but doesn't, but that's not a fault in D. The inliner could be improved.
Re: Example of Rust code
On 8/10/2012 5:19 PM, David Piepgrass wrote: Your version is basically a very long-winded way to say auto x = 5 - (3 + 1); so it really has nothing to do with the example. The point of the example was to represent a simple AST and store it on the stack, not to represent + and - operators as plus() and minus() functions. I see that now, and I presented a D way of doing it using expression templates, a technique pioneered by C++ programmers. Expression templates have been used before in D, in particular to implement a regular expression engine. Dmitry Olshansky has since shown how fantastic this is for generating incredibly fast regex engines. As far as I know, only C++ and D have sufficiently powerful abstraction mechanisms to make this possible. Really? And here I genuinely thought D was good enough for all the things C and FORTRAN are used for. If you're going to write C code, use a C compiler. It's possible to use D as a C compiler, but kinda pointless except as a transitory state towards using D capabilities. I hope someday to have a programming system whose features are not limited to whatever features the language designers saw fit to include -- a language where the users can add their own features, all the while maintaining native efficiency like D. That language would potentially allow Rust-like code, D-like code, Ruby-like code and even ugly C-like code. I guess you don't want to be the one to kickstart that PL. I've been planning to do it myself, but so far the task seems just too big for one person. Andrei originally proposed to me a language like that. I talked him out of it :-)
Re: An example of Rust code
On 10-08-2012 03:44, bearophile wrote: Sorry, my mistake again, this was meant for the main D newsgroup. Bye, bearophile Please repost on the main newsgroup so it gets attention. :) -- Alex Rønne Petersen a...@lycus.org http://lycus.org
Re: An example of Rust code
Alex Rønne Petersen: Please repost on the main newsgroup so it gets attention. :) OK. But I don't know much much interested they will be :-) Bye, bearophile
An example of Rust code
Through Reddit I've found a page that shows a small example of Rust code: http://www.reddit.com/r/programming/comments/xyfqg/playing_with_rust/ https://gist.github.com/3299083 The code: https://gist.github.com/3307450 - So I've tried to translate this first part of the Rust code to D (I have not run it, but it looks correct): enum expr { val(int), plus(expr, expr), minus(expr, expr) } fn eval(e: expr) - int { alt *e { val(i) = i, plus(a, b) = eval(a) + eval(b), minus(a, b) = eval(a) - eval(b) } } fn main() { let x = eval( minus(val(5), plus(val(3), val(1; io::println(#fmt(val: %i, x)); } A comment from the little article: putting a in front of a expression allocates it on the stack and gives you a reference to it. so the lifetime of this tree is to the end of the run [main] function. - The first D version is easy enough to write, but: - It uses classes, I think each class instance uses more memory than what's used in the original Rust code. - All the class instances here are allocated on the Heap. This is less efficient than the Rust code, where all the data is stack-allocated. - This code contains boilerplate, it's long. - Writing eval() is easy, but in the first version of eval() there were two bugs. - The assert(0) in eval() is not nice. There is no compile-time safety. - The several dynamic casts in eval() are slow. interface Expr {} class Val : Expr { const int v; this(in int v_) pure nothrow { this.v = v_; } } class Plus : Expr { const Expr x, y; this(in Expr x_, in Expr y_) pure nothrow { this.x = x_; this.y = y_; } } class Minus : Expr { const Expr x, y; this(in Expr x_, in Expr y_) pure nothrow { this.x = x_; this.y = y_; } } int eval(in Expr e) pure nothrow { if (Val ve = cast(Val)e) return ve.v; else if (Plus pe = cast(Plus)e) return eval(pe.x) + eval(pe.y); else if (Minus me = cast(Minus)e) return eval(me.x) - eval(me.y); else assert(0); } void main() { auto ex = new Minus(new Val(5), new Plus(new Val(3), new Val(1))); import std.stdio; writeln(Val: , eval(ex)); } - This second D version uses the same class definitions, but allocates the class instances on the stack. The code is bug prone and ugly. The other disadvantages are unchanged: void main() { import std.stdio; import std.conv: emplace; import core.stdc.stdlib: alloca; enum size_t size_Val = __traits(classInstanceSize, Val); enum size_t size_Plus = __traits(classInstanceSize, Plus); enum size_t size_Minus = __traits(classInstanceSize, Minus); Val e1 = emplace!Val(alloca(size_Val)[0 .. size_Val], 5); Val e2 = emplace!Val(alloca(size_Val)[0 .. size_Val], 3); Val e3 = emplace!Val(alloca(size_Val)[0 .. size_Val], 1); Plus e4 = emplace!Plus(alloca(size_Plus)[0 .. size_Plus], e2, e3); Minus ex2 = emplace!Minus(alloca(size_Minus)[0 .. size_Minus], e1, e4); writeln(Val: , eval(ex2)); } - A third D version, using tagged structs: - It doesn't look nice, and it's long. - Class references can be null, so I have added tests at runtime in the pre-conditions. In the Rust code the references can't be null. - The structs are stack-allocated but the main() code is not nice. - The tags can't const or immutable, otherwise the compiler doesn't read the actual value of the various tags, assuming it's always Tag.none. - Too many casts make this code bug-prone. import std.stdio; enum Tag { none, val, plus, minus } struct Expr { Tag tag = Tag.none; } struct Val { Tag tag = Tag.val; immutable int v; this(int v_) pure nothrow { this.v = v_; } } struct Plus { Tag tag = Tag.plus; const Expr* x, y; this(in Expr* x_, in Expr* y_) pure nothrow in { assert(x_ != null); assert(y_ != null); } body { this.x = x_; this.y = y_; } } struct Minus { Tag tag = Tag.minus; const Expr* x, y; this(in Expr* x_, in Expr* y_) pure nothrow in { assert(x_ != null); assert(y_ != null); } body { this.x = x_; this.y = y_; } } int eval(in Expr* e) pure nothrow in { assert(e); } body { final switch (e.tag) { case Tag.none: assert(0); case Tag.val: return (cast(Val*)e).v; case Tag.plus: auto pe = cast(Plus*)e; return eval(pe.x) + eval(pe.y); case Tag.minus: auto me = cast(Minus*)e; return eval(me.x) - eval(me.y); } } void main() { const e1 = Val(5); const e2 = Val(3); const e3 = Val(1); const e4 = Plus(cast(Expr*)e2, cast(Expr*)e3
Re: An example of Rust code
Sorry, my mistake again, this was meant for the main D newsgroup. Bye, bearophile