Skip to content

Instantly share code, notes, and snippets.

@ErichDonGubler
Last active January 23, 2018 19:13
Show Gist options
  • Save ErichDonGubler/b9bb2c17cbd95157e39a517c068e22ab to your computer and use it in GitHub Desktop.
Save ErichDonGubler/b9bb2c17cbd95157e39a517c068e22ab to your computer and use it in GitHub Desktop.
Response to "Methods and UFCS"'s constraints point againt D at @MaikKlein's https://maikklein.github.io/post/cmp-rust-d/
/**
* Demo: duckConstraints.d
* Purpose: Demonstrate duck-typed constraints in D (in response to https://maikklein.github.io/2016-03-01-metaprogramming-typeobject/)
* Authors: Erich Gubler (Github: @erichdongubler)
* Date: 6/25/2016
*
* @maikklein: "Constrains" ( ;) ) in D similar to the Rust block you have shown:
*
* impl<T> Bar<T>
* where: T: Copy
* {
* fn something(&self, val: T)
* {
* // ...
* }
* }
*
* ...can be implemented in several ways, as shown below. You can use `rdmd --build-only --main -unittest duckConstraints.d` to test this.
*/
interface Copy
{
Copy dup();
}
class DupableString : Copy // some implementation of Copy
{
string s = "";
override DupableString dup()
{
auto newDS = new DupableString;
newDS.s = this.s.dup;
return newDS;
}
}
// As you mentioned before, we could use dynamic dispatch to resolve calling because we're using an interface:
class Foo
{
void something(Copy val)
{
// ...
}
}
// But the point is that we want static dispatch! We want zero overhead! :)
// We can do compile-time constraints with a declaration if, which is Rust's `where` functionality and more:
class Bar
{
void something(T)(T val) // Interface-based version (equivalent to Rust example above)
if(is(T: Copy))
{
pragma(msg, " Using Copy implementation for Bar.something");
}
void something_else(T)(T val) // Duck-typed version
if(__traits(compiles, val.dup))
{
pragma(msg, " Using duck-typing implementation for Bar.something_else");
}
unittest
{
pragma(msg, "Doing Bar test");
auto b = new Bar;
b.something(new DupableString);
b.something_else("asdf");
static assert(!__traits(compiles, b.something("asdf")));
static assert(!__traits(compiles, b.something(42))); // This shouldn't work, because there's no .dup method by default for intstatic assert(__traits(compiles, b.something_else(new DupableString)));
}
}
//...but from what I see, it's more understandable to enforce an interface with static if.
class Bar2
{
void something(T)(T val)
{
static if(is(T : Copy)) // Static if can be used for multiple conditions if necessary
{
pragma(msg, " Using Copy implementation for Bar2.something");
auto stuff = val.dup;
}
else // You can do even more conditions here -- not just use interfaces in `is` expressions!
{
pragma(msg, " Using duck-typing implementation for Bar2.something");
auto stuff = val.dup; // implicitly checked with duck-typing constraints
}
}
unittest
{
pragma(msg, "Doing Bar2 test");
auto b2 = new Bar2;
b2.something(new DupableString);
b2.something("asdf");
static assert(!__traits(compiles, b2.something(42))); // This shouldn't work, because there's no .dup method by default for int
}
}
// But in D, we prefer simple duck typing, which would probably look more like:
class Bar3
{
void something(T)(T val)
{
auto stuff = val.dup; // implicitly checked with duck-typing constraints
}
unittest
{
pragma(msg, "Doing Bar3 test");
auto b3 = new Bar3;
b3.something("asdf");
static assert(!__traits(compiles, b3.something(42))); // This shouldn't work, because there's no .dup method by default for int
}
}
// See how much more concise that is? :) We didn't need an interface or trait at all!
// In Rust, we're stuck to using traits to control constraints, but we can use any compile-time conditions in D.
// I'd chalk better constraints to D!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment