May 02, 2018
On 04/30/2018 05:35 PM, H. S. Teoh wrote:
> 
> Also, design by introspection.  Dependence on explicit types is so last
> century.  Design by introspection FTW!  Decoupling your code from
> explicit types makes it more encapsulated, and gives you incentive to
> write more defensively, resulting in better, more change-resilient code.
> When an upstream library changes a return type, you can just recompile
> and go, rather than waste time patching the 150 different places where
> the explicit type was named.  Let the machine do the work for you!
> 

There's a lot about that I like too, but I really wish D didn't use structral typing to do it. I still think the only reason D's structral typing hasn't blown up in our faces is because it's still mostly limited to the basic ranges isn't not a prevalent part of most internal and external APIs. Structural typing basically amounts to a form of global namespace (which can also work out ok *if used sparingly*, not that I'd want to try). At first, D thankfully killed off the global namespace for the most part...but then it brings its pitfalls right back in the form of structural typing. Grrr...

Structral typing isn't "If it walks like a duck and quacks like a duck...". Structural typing is "If it walks and it talks, then it must be a duck, because ducks walk and have their own form of talk, so clearly anything that walks and talks must be a duck."
May 02, 2018
On Wednesday, 2 May 2018 at 00:01:42 UTC, Nick Sabalausky wrote:
> Now, all that said, using auto for a function signature's return type shouldn't usually be done, except in very careful, specific "voldemort type" kinds of situations (and even then, I dont see a real big point).

I do it all the time because of attribute inference. D has way to many attributes, so I'm willing to automatically let the Compiler interfere which are appropriate, he knows it better anyway.
May 02, 2018
On Wed, May 02, 2018 at 12:11:57AM -0400, Nick Sabalausky (Abscissa) via Digitalmars-d wrote:
> On 04/30/2018 05:35 PM, H. S. Teoh wrote:
> > 
> > Also, design by introspection.  Dependence on explicit types is so last century.  Design by introspection FTW!  Decoupling your code from explicit types makes it more encapsulated, and gives you incentive to write more defensively, resulting in better, more change-resilient code.  When an upstream library changes a return type, you can just recompile and go, rather than waste time patching the 150 different places where the explicit type was named.  Let the machine do the work for you!
> > 
> 
> There's a lot about that I like too, but I really wish D didn't use structral typing to do it. I still think the only reason D's structral typing hasn't blown up in our faces is because it's still mostly limited to the basic ranges isn't not a prevalent part of most internal and external APIs. Structural typing basically amounts to a form of global namespace (which can also work out ok *if used sparingly*, not that I'd want to try).  At first, D thankfully killed off the global namespace for the most part...but then it brings its pitfalls right back in the form of structural typing. Grrr...

I've been using structural typing / design by introspection to good success in my latest project, actually.  I was writing a submodule that handled interfacing with POVRay for 3D rendering (non-realtime, obviously), and wanted a clean API that didn't force tons of boilerplate on you.  The main problem here is that POVRay has so many features, knobs, and dials that either you have to dumb things down so much that you're no longer using most of its features, which defeats the purpose of using it(!), or you have to essentially reinvent SDL in your API so that your users have access to everything they might need, which makes your API so complex and hairy it would require Java-style verbosity, complete with factory classes, wrapper types, a class hierarchy, and gratuitousUnreasonablyLongIdentifiers, just to initialize it to do something as simple as rendering a couple of polygons.

Eventually, I settled on DoI by specifying some basic required properties for, say, polygons to be output as a mesh, y'know, vertex coordinates and lists of vertex indices for the polygons, with many optional parameters checked by static if.  So if you wanted just to output a couple of polygons with flat textures, all you have to do is to pass in an array of vectors and an array of int[]'s, and off you go.  If you wanted different textures for each polygon, make a struct that aliases int[] to this (or overloads opIndex) plus a .texture field to specify the texture.  If you wanted surface normal interpolation, wrap your vectors in a struct that has a .normal field specifying the normals to interpolate.  On the implementation side, it's just a bunch of static if's that optionally checks if a property is present, and provide implementation for it if it is.

If it weren't for DoI, I would've ended up with a big, complicated API with tons of boolean switches or a class hierarchy to provide for all the variations that might be needed, and it would have required a ton of boilerplate just to output a couple of flat polygons.  With DoI, I get to scale the amount of initialization code I need according to how many features I will actually use.


> Structral typing isn't "If it walks like a duck and quacks like a duck...".  Structural typing is "If it walks and it talks, then it must be a duck, because ducks walk and have their own form of talk, so clearly anything that walks and talks must be a duck."

How else would you do DoI, though?  With Concepts?  The advantage of using structural typing over concepts for DoI is that you would need an exponential number of concepts to catch up with a linear number of optional fields in a structural typing model.  Sure, structural typing has its warts, but it's at least more scalable in this respect.


T

-- 
What do you call optometrist jokes? Vitreous humor.
May 02, 2018
On Wednesday, 2 May 2018 at 14:05:49 UTC, H. S. Teoh wrote:
> How else would you do DoI, though?  With Concepts?  The advantage of using structural typing over concepts for DoI is that you would need an exponential number of concepts to catch up with a linear number of optional fields in a structural typing model.  Sure, structural typing has its warts, but it's at least more scalable in this respect.
>
>
> T

Why? I don't think concepts force you to write a specific implementation for every possible combination of compile-time values.
May 02, 2018
On Wednesday, 2 May 2018 at 14:05:49 UTC, H. S. Teoh wrote:
> How else would you do DoI, though?  With Concepts?  The advantage of using structural typing over concepts for DoI is that you would need an exponential number of concepts to catch up with a linear number of optional fields in a structural typing model.  Sure, structural typing has its warts, but it's at least more scalable in this respect.

UDAs to the rescue!

class JackInTheBox
{
  // Which way is it facing?
  Direction front();
  // Pop the box open and have the doll spring out toward the front side
  void popFront();
  // Remove everything from the box. Return true if stuff was removed.
  bool empty();
}

We're in for a bad time.


@Range
class JackInTheBox
{
  ...
}

Here I just shot myself in the foot.


// A document is a range of pages, maybe.
@Range
struct Document
{
  // saves to disk
  SaveResults save();
}

This is a subtler problem.


struct Document
{
  @Range
  SaveResults save();
}

I shot myself in the foot again.


It's analogous to explicit interface inheritance in C#. Thanks to D's options for applying annotations to a block or in label style, it wouldn't be terribly onerous.
May 03, 2018
On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
> I'll freely admit I haven't put a ton of thought into this post (never a good start), however I'm genuinely curious what people's feeling are with regards to the auto keyword.
>
> So I'm curious, what's the consensus on auto?

I'll start with auto for declaration and then talk about auto for return types.

The main issue with utilizing auto is that a type will lead you to documentation on the capabilities of the variable and to locate the implementation of the code being called. The desire to decouple from the concrete type is admirable but generally the desire for the concrete type isn't about coupling with it.

What I find though is that auto doesn't increase the difficulty of the objectives I described. What I find is in other languages (generally C# and Java) is that you won't be returned a concrete type but an interface. This can be nice for capabilities of the variable but all to often I find that the API still expects you to convert to a concrete type. And you still need to track down the actually concrete type to review the actual implementation of that function.

I find auto to be very useful for development as I can switch out the type be it a class/struct or interface rather easily. I have mixed feelings when it comes to debugging. For a stable and unchanging interface (concept not construct) it should be avoided.

Use of auto in return type I also like but use sparingly. This is because it hides documentation of an API. And I'm not talking about the function documentation laking specification of what it returns I'm talking about types being a good way to make a point of reference. For example an auto function which returns a range can easily reference the Range API since it is the D interface for iteration. Custom/framework types don't have that luxury.
May 03, 2018
On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
> So I'm curious, what's the consensus on auto?

Speaking for myself:

1) I find auto to be useful when instantiating objects locally; eliminates redundancy, noise, and line size.

2) I avoid auto functions and despise them in API documentation. It’s difficult to work with the return type of a function when the definition is auto and the author didn’t follow up by describing exactly what was returned.

3) I avoid auto for primitive types because their type names are short, and declaring them C- and Ada-style at the beginning of a local block helps massively clarify what a function is about to do.

So basically only for object instantiations. I’ve yet to find a great use for it outside of that.
May 04, 2018
On 05/02/2018 10:05 AM, H. S. Teoh wrote:
> 
> I've been using structural typing / design by introspection to good
> success in my latest project, actually.

I don't doubt that. Similar to global namespace, if the structural typing is only used heavily by one or two components of a project (ie, libs, etc), then it's not too difficult to avoid problems. The real danger and problems come when a project uses several components that all make heavy use of either a global namespace (which D luckily doesn't really have) or structural typing.

>> Structral typing isn't "If it walks like a duck and quacks like a
>> duck...".  Structural typing is "If it walks and it talks, then it
>> must be a duck, because ducks walk and have their own form of talk, so
>> clearly anything that walks and talks must be a duck."
> 
> How else would you do DoI, though?  With Concepts?  The advantage of
> using structural typing over concepts for DoI is that you would need an
> exponential number of concepts to catch up with a linear number of
> optional fields in a structural typing model.  Sure, structural typing
> has its warts, but it's at least more scalable in this respect.

With a slight variation on structural typing that's augmented with mandatory opt-in. I'll explain:

The problem with structural typing is that it's unable to distinguish between intended matches and accidental, unintended matches. This is because it doesn't REQUIRE types/functions/etc to clearly state, "Yes, I *intend* to be implementing interface XYZ". Or perhaps more accurately: "Yes, I *intend* for this to satisfy isXYZ!T". (Where "isXYZ" might be something like "isInputRange" or "isInfiniteRange", or both, etc.)

Note the all-caps "REQUIRE" in the paragraph above. That is to say, it's not enough for the developer of struct Foo to toss in...

static assert(isXYZ!Foo);

...because the problem doesn't lie with the "intended matches". The problem lies with the "unintended matches". And that static assert does nothing to help isXYZ figure out that some other type, Bar, from some other package, is NOT deliberately designed to satisfy isXYZ even if it just happens to *look* like it satisfies it purely by chance.

As an aside: Think you're not likely to hit false positives with structural typing? Well, in all honestly, unless you're really sloppy, you're not likely hit name collisions in the global namespace either...UNTIL you reach the point where projects are composed of many third-party packages, and most third party packages start using the global namespace in their own way. Then it becomes a distinct possibility.

The same dynamic applies here because, like global namespaces, structural typing (by default) has no mechanism for scope-limiting or compartmentalization, and (intentionally) operates on unqualified names.

We address the global namespace's lack of scope-limiting and compartmentalization through...well, namespaces. The namespaces may be implied by classes (old-school Java), by compilation units (D), or specified manually (C++). But how do we address structural typing's lack of scope-limiting and compartmentalization? Currently, we don't. And that's the problem.

So back you your question: How else would you do DoI?

Answer: By making isXYZ!T reject all T which DO NOT satisfy a deliberate, cannot-be-an-accident, condition of isXYZ's choosing.

Thus, type T *cannot* satisfy isXYZ without T's developer saying "Yes, I hereby certify it is my deliberate intention that T satisfies isXYZ and that, if it does satisfy, it is by my own intention and not by coincidental happenstance."

The exact details of this condition aren't terribly important, but I like Neia's suggestion of utilizing UDAs for this purpose. An old idea I had before UDAs existed was to require a dummy member enum named something like _satisfies_module_foo_bar_isXYZ, which of course would be abstracted away by something more convenient...a mixin or such (but nobody seemed remotely interested). But I like the UDA idea better.
May 04, 2018
On Friday, 4 May 2018 at 04:12:09 UTC, Nick Sabalausky (Abscissa) wrote:
> On 05/02/2018 10:05 AM, H. S. Teoh wrote:
>> [...]
>
> I don't doubt that. Similar to global namespace, if the structural typing is only used heavily by one or two components of a project (ie, libs, etc), then it's not too difficult to avoid problems. The real danger and problems come when a project uses several components that all make heavy use of either a global namespace (which D luckily doesn't really have) or structural typing.
>
> [...]

Have you seen Atila's concepts library?
May 04, 2018
On 05/04/2018 03:56 AM, Laeeth Isharc wrote:
> On Friday, 4 May 2018 at 04:12:09 UTC, Nick Sabalausky (Abscissa) wrote:
>> On 05/02/2018 10:05 AM, H. S. Teoh wrote:
>>> [...]
>>
>> I don't doubt that. Similar to global namespace, if the structural typing is only used heavily by one or two components of a project (ie, libs, etc), then it's not too difficult to avoid problems. The real danger and problems come when a project uses several components that all make heavy use of either a global namespace (which D luckily doesn't really have) or structural typing.
>>
>> [...]
> 
> Have you seen Atila's concepts library?

Yea, last I checked, it doesn't address what I'm talking about at all. That lib's basically just this:

struct Foo {...}
static assert(isXYX!Foo);

But with more detailed diagnostics. (Plus it can help you write the isXYZ function.)

Unless something's changed since last looked at it, it didn't address my #1 key point: If struct Foo's author *doesn't* include the lib's "@models!(Foo, isFoo)", then isFoo!Foo should return false.