Re: How Different Are Templates from Generics

2019-10-12 Thread Jonathan M Davis via Digitalmars-d-learn
On Saturday, October 12, 2019 9:48:02 PM MDT jmh530 via Digitalmars-d-learn 
wrote:
> On Saturday, 12 October 2019 at 21:44:57 UTC, Jonathan M Davis
>
> wrote:
> > [snip]
>
> Thanks for the reply.
>
> As with most people, I don't write a lot of D code that uses
> classes that much.
>
> The use case I'm thinking of is with allocators, which - to be
> honest - is not something I deal with much in my own code.
> Basically, some of the examples have stuff like
> ScopedAllocator!Mallocator, which would imply that there is a
> different ScopedAllocator for each allocator. However, if you
> apply Java's generics, then you would just have one. Not sure if
> it would make any kind of difference in real-life code, but still
> interesting to think about.

I wouldn't think that there would be enough different allocator types to
matter much. Certainly, the amount of code that gets generated by templates
for dealing with stuff like ranges would dwarf it. If program size really
becomes a problem, then examining how code uses templates and trying to
reduce how much they're used could certainly have an impact, but I'd expect
it to be fairly rare that attempting to emulate Java's generics would help
much - especially since it would only work when classes were involved. The
main place that such an approach would have much chance of having an impact
would be with regards to container implementations when the code puts a lot
of different types of class objects inside of containers, and even that
would easily be dwarfed by all of the other template usage in your typical D
program. For Java's approach to make much sense, you'd probably have to be
writing very Java-like code.

- Jonathan M Davis





Re: How Different Are Templates from Generics

2019-10-12 Thread jmh530 via Digitalmars-d-learn
On Saturday, 12 October 2019 at 21:44:57 UTC, Jonathan M Davis 
wrote:

[snip]



Thanks for the reply.

As with most people, I don't write a lot of D code that uses 
classes that much.


The use case I'm thinking of is with allocators, which - to be 
honest - is not something I deal with much in my own code. 
Basically, some of the examples have stuff like 
ScopedAllocator!Mallocator, which would imply that there is a 
different ScopedAllocator for each allocator. However, if you 
apply Java's generics, then you would just have one. Not sure if 
it would make any kind of difference in real-life code, but still 
interesting to think about.


Re: How Different Are Templates from Generics

2019-10-12 Thread Jonathan M Davis via Digitalmars-d-learn
On Saturday, October 12, 2019 2:11:28 PM MDT jmh530 via Digitalmars-d-learn 
wrote:
> On Friday, 11 October 2019 at 17:50:42 UTC, Jonathan M Davis
>
> wrote:
> > [snip]
>
> A very thorough explanation!
>
> One follow-up question: would it be possible to mimic the
> behavior of Java generics in D?

Yes, but it's unlikely that it would make any sense to do so. You'd
basically have to do something like

auto foo(T)(T obj)
if(is(T : Object) && !is(T == Object))
{
return foo(cast(Object)obj);
}

auto foo(Object obj)
{
...
}

And for containers, you'd basically end up with a templated container that
was just a wrapper around a non-templated container that operated on Object.

If you went with such an approach, you'd get less code in the binary, but
you'd also end up with a deeper call stack because of all of the wrappers
needed to add the casts for you.

However, since Object can't do much of anything, having code that operates
on Object isn't usually very useful. You could have the code use a different
base class that had whatever operations you wanted, but you're still adding
a fair bit of extra machinery just to avoid a few template instantiations.

And since idiomatic D doesn't use classes much (rather, best practice is to
use a struct unless you need polymorphism or you need something to always be
a reference type), and it uses templates quite heavily (that's especially
true with range-based code), it would be pretty bizarre to try and use
Java's approach in D.

- Jonathan M Davis





Re: How Different Are Templates from Generics

2019-10-12 Thread jmh530 via Digitalmars-d-learn
On Friday, 11 October 2019 at 17:50:42 UTC, Jonathan M Davis 
wrote:

[snip]


A very thorough explanation!

One follow-up question: would it be possible to mimic the 
behavior of Java generics in D?


Re: How Different Are Templates from Generics

2019-10-11 Thread Ahmet Sait via Digitalmars-d-learn
On Friday, 11 October 2019 at 17:50:42 UTC, Jonathan M Davis 
wrote:
Generic functions and types operate on Object underneath the 
hood. If you have Container and Container, you really 
just have Container with some syntactic niceties to 
avoid explicit casts. You get type checks to ensure that 
Container isn't given a Bar unless Bar is derived from 
Foo, and the casts to and from Object when giving 
Container a Foo are taken care of for you, but it's still 
always Container underneath the hood.


In the case of Java, the type of T in Container or foo() 
is truly only a compile time thing, so the bytecode only has 
Container and no clue what type is actually supposed to 
be used (the casts are there where the container or function is 
used, but the container or function has no clue what the type 
is; it just sees Object). That makes it possible to cheat with 
reflection and put something not derived from Foo in 
Container but will then usually result in runtime failures 
when the casts the compiler inserted are run. C# doesn't have 
that kind of type erasure in that the information that 
Container contains Foo rather than Object is maintained at 
runtime, but you still have a Container. It's just a 
Container with some metadata which keeps track of the 
fact that for this particular object of Container, 
Object is always supposed to be a Foo. As I'm a lot less 
familiar with C# than Java, I'm not all that familiar with what 
the practical benefits that gives are, though I'd expect that 
it would mean that reflection code would catch when you're 
trying to put a Bar into Container and wouldn't let you.


Note that for generics to work, they have to a common base 
type, and you only ever get one version of a generic class or 
function even if it gets used with many different types derived 
from Object. For a primitive type like int or float (as well as 
for structs in the case of C#), they have to be put into a type 
derived from Object in order to be used with generics (as I 
expect you're aware, C# calls this boxing and unboxing). 
Templates don't act like this at all.


Unlike Java, C# actually does generate different code pieces for 
different value types [1] and reuses the same generated code for 
reference types.


[1] 
https://alexandrnikitin.github.io/blog/dotnet-generics-under-the-hood/


Re: How Different Are Templates from Generics

2019-10-11 Thread Jonathan M Davis via Digitalmars-d-learn
On Friday, October 11, 2019 12:09:20 PM MDT Just Dave via Digitalmars-d-
learn wrote:
> Thanks for the thorough explanation. Most of that is how I was
> thinking it worked. However, that leaves me perplexed. If
> templates just generate code then how come:
>
> Wouldnt..
>
>  class SomeClass(T) : ISomeInterface!T
>
> and..
>
>  class SomeOtherClass(T) : ISomeInterface!T
>
> ...generate two different interfaces? Two interfaces that do the
> same thing, but two interfaces nonetheless? I assume each type in
> D has some form of type id underlying everything, which wouldn't
> that make the follow:
>
>  if (instance1 is ISomeInterface)
>  {
>  Console.WriteLine("Instance1 is interface!");
>  }
>
> fail? Or is there some extra magic that is making it work with my
> experiments?

You get a different template instantiation for each set of template
arguments. So, if you have ISomeInterface!int, and you use
ISomeinterface!int somewhere else, because they're both instantiating
ISomeInterface with the same set of template arguments, you only get one
instantiation. So,

class SomeClass : ISomeInterface!int

and

class SomeOtherClass : ISomeInterface!int

would both be implementing the exact same interface. And if you then have

class SomeClass(T) : ISomeInterface!T

and

class SomeOtherClass(T) : ISomeInterface!T

then SomeClass!int and SomeOtherClass!int would both be implementing the
same interface, because in both cases, it would be ISomeInterface!int.
SomeClass!int and SomeOtherClass!float would not be implementing the same
interface, because it would be ISomeInterface!int and ISomeInterface!float,
but ISomeInterface!int doesn't result in multiple instantiations even if
it's used in different parts of the code.

- Jonathan M Davis





Re: How Different Are Templates from Generics

2019-10-11 Thread Just Dave via Digitalmars-d-learn
Thanks for the thorough explanation. Most of that is how I was 
thinking it worked. However, that leaves me perplexed. If 
templates just generate code then how come:


Wouldnt..

class SomeClass(T) : ISomeInterface!T

and..

class SomeOtherClass(T) : ISomeInterface!T

...generate two different interfaces? Two interfaces that do the 
same thing, but two interfaces nonetheless? I assume each type in 
D has some form of type id underlying everything, which wouldn't 
that make the follow:


if (instance1 is ISomeInterface)
{
Console.WriteLine("Instance1 is interface!");
}

fail? Or is there some extra magic that is making it work with my 
experiments?


Re: How Different Are Templates from Generics

2019-10-11 Thread Jonathan M Davis via Digitalmars-d-learn
On Friday, October 11, 2019 8:43:49 AM MDT Just Dave via Digitalmars-d-learn 
wrote:
> I come from both a C++ and C# background. Those have been the
> primary languages I have used. In C# you can do something like
> this:
>
>  public interface ISomeInterface
>  {
>   T Value { get; }
>  }
>
>  public class SomeClass : ISomeInterface
>  {
>   T Value { get; set; }
>  }
>
>  public class SomeOtherClass : ISomeInterface
>  {
>  T Value { get; set; }
>  }
>
>  public static class Example
>  {
>  public static void Foo()
>  {
>  var instance1 = new SomeClass(){ Value = 4; };
>  var instance2 = new SomeClass(){ Value = 2; };
>
>  if (instance1 is ISomeInterface)
>  {
>  Console.WriteLine("Instance1 is interface!");
>  }
>
>  if (instance2 is ISomeInterface)
>  {
>  Console.WriteLine("Instance2 is interface!");
>  }
>  }
>  }
>
> Expected output is both WriteLines get hit:
>
>  Instance1 is interface!
>
>  Instance2 is interface!
>
>
> So now the 'D' version:
>
>  interface ISomeInterface(T)
>  {
>   T getValue();
>  }
>
>  class SomeClass(T) : ISomeInterface!T
>  {
>  private:
>   T t;
>
>  public:
>   this(T t)
>   {
>  this.t = t;
>   }
>
>   T getValue()
>   {
> return t;
>   }
>  }
>
>  class SomeOtherClass(T) : ISomeInterface!T
>  {
>  private:
>   T t;
>
>  public:
>   this(T t)
>   {
>  this.t = t;
>   }
>
>   T getValue()
>   {
>   return t;
>   }
>  }
>
> ...which seems to work the same way with preliminary testing. I
> guess my question is...templates are different than generics, but
> can I feel confident continuing forward with such a design in D
> and expect this more or less to behave as I would expect in C#?
> Or are there lots of caveats I should be aware of?

Generics and templates are syntactically similiar but are really doing very
different things.

Generic functions and types operate on Object underneath the hood. If you
have Container and Container, you really just have
Container with some syntactic niceties to avoid explicit casts. You
get type checks to ensure that Container isn't given a Bar unless Bar
is derived from Foo, and the casts to and from Object when giving
Container a Foo are taken care of for you, but it's still always
Container underneath the hood.

In the case of Java, the type of T in Container or foo() is truly only
a compile time thing, so the bytecode only has Container and no clue
what type is actually supposed to be used (the casts are there where the
container or function is used, but the container or function has no clue
what the type is; it just sees Object). That makes it possible to cheat with
reflection and put something not derived from Foo in Container but
will then usually result in runtime failures when the casts the compiler
inserted are run. C# doesn't have that kind of type erasure in that the
information that Container contains Foo rather than Object is
maintained at runtime, but you still have a Container. It's just a
Container with some metadata which keeps track of the fact that for
this particular object of Container, Object is always supposed to be
a Foo. As I'm a lot less familiar with C# than Java, I'm not all that
familiar with what the practical benefits that gives are, though I'd expect
that it would mean that reflection code would catch when you're trying to
put a Bar into Container and wouldn't let you.

Note that for generics to work, they have to a common base type, and you
only ever get one version of a generic class or function even if it gets
used with many different types derived from Object. For a primitive type
like int or float (as well as for structs in the case of C#), they have to
be put into a type derived from Object in order to be used with generics (as
I expect you're aware, C# calls this boxing and unboxing). Templates don't
act like this at all.

Templates are literally templates for generating code. A template is nothing
by itself. Something like

struct Container(T)
{
T[] data;
}

or

T foo(T)(T t)
{
return t;
}

doesn't result in any code being in the binary until unless template is
instantiated with a specific type, and when that template is instantiated,
code is generated based on the type that it's instantiated with. So,
Container!int and Container!Foo result in two different versions of
Container being generated and put in the binary - one which operates on int,
and one which operates on Foo. There is no conversion to Object going on
here. The code literally uses int and Foo directly and is generated
specifically for those types. Not only does that mean that the generated
code can be optimized for the specific type rather than being for any
Object, but it also means that the code itself could do some

Re: How Different Are Templates from Generics

2019-10-11 Thread Tobias Pankrath via Digitalmars-d-learn

On Friday, 11 October 2019 at 14:43:49 UTC, Just Dave wrote:
I come from both a C++ and C# background. Those have been the 
primary languages I have used.


Probably the D templates relate to C# generics the same way that 
C++ templates do.


How Different Are Templates from Generics

2019-10-11 Thread Just Dave via Digitalmars-d-learn
I come from both a C++ and C# background. Those have been the 
primary languages I have used. In C# you can do something like 
this:


public interface ISomeInterface
{
 T Value { get; }
}

public class SomeClass : ISomeInterface
{
 T Value { get; set; }
}

public class SomeOtherClass : ISomeInterface
{
T Value { get; set; }
}

public static class Example
{
public static void Foo()
{
var instance1 = new SomeClass(){ Value = 4; };
var instance2 = new SomeClass(){ Value = 2; };

if (instance1 is ISomeInterface)
{
Console.WriteLine("Instance1 is interface!");
}

if (instance2 is ISomeInterface)
{
Console.WriteLine("Instance2 is interface!");
}
}
}

Expected output is both WriteLines get hit:

Instance1 is interface!

Instance2 is interface!


So now the 'D' version:

interface ISomeInterface(T)
{
T getValue();
}

class SomeClass(T) : ISomeInterface!T
{
private:
T t;

public:
this(T t)
{
this.t = t;
}

T getValue()
{
   return t;
}
}

class SomeOtherClass(T) : ISomeInterface!T
{
private:
T t;

public:
this(T t)
{
this.t = t;
}

T getValue()
{
return t;
}
}

...which seems to work the same way with preliminary testing. I 
guess my question is...templates are different than generics, but 
can I feel confident continuing forward with such a design in D 
and expect this more or less to behave as I would expect in C#? 
Or are there lots of caveats I should be aware of?