Thread overview
scoped classes and dependency inversion
Nov 08, 2018
Sjoerd Nijboer
Nov 08, 2018
Alex
Nov 08, 2018
Sjoerd Nijboer
Nov 08, 2018
Alex
Nov 08, 2018
Neia Neutuladh
Nov 08, 2018
Neia Neutuladh
Nov 08, 2018
Sjoerd Nijboer
Nov 09, 2018
Sjoerd Nijboer
Nov 09, 2018
Alex
Nov 09, 2018
Sjoerd Nijboer
November 08, 2018
I'm trying to invert the dependency from the classes `Bar -> Foo` to `Foo -> IFoo <- Bar` at compile time.

I do want `Foo's` to be embedded into `Bar`

So silly me tried something like this:
module main;

```import std.stdio;
import std.typecons;

void main()
{
	auto bar = new Bar!(scoped!Foo());
}

class Bar (TFoo)
	if(is(TFoo == IFoo))
{
	scoped!TFoo _foo;
	this(scoped!TFoo foo)
	{
		_foo = foo;
	}
}

class Foo : IFoo
{
	void baz(){}
}

interface IFoo
{
	void baz();
}```

This fails to compile on line 8 since you can't move a scoped!Foo.
So how can I delay the construction of scoped!Foo that it'll end up inside Bar?
If there is a more elegant solution I'm also interested.
November 08, 2018
On Thursday, 8 November 2018 at 11:04:19 UTC, Sjoerd Nijboer wrote:
> I'm trying to invert the dependency from the classes `Bar -> Foo` to `Foo -> IFoo <- Bar` at compile time.
>
> I do want `Foo's` to be embedded into `Bar`
>
> So silly me tried something like this:
> [...]
>
> So how can I delay the construction of scoped!Foo that it'll end up inside Bar?
> If there is a more elegant solution I'm also interested.

Hmm... not sure, if I got your idea... Do you think about something like this?

´´´
import std.stdio;
import std.typecons;

void main()
{
	auto bar = new Bar!Foo();
}

class Bar(TFoo) if(is(TFoo : IFoo))
{
	typeof(scoped!TFoo()) _foo;
	this()
	{
		_foo = scoped!TFoo();
	}
}

class Foo : IFoo { void baz(){} }
interface IFoo{ void baz(); }
´´´


November 08, 2018
On Thursday, 8 November 2018 at 12:45:57 UTC, Alex wrote:
> Hmm... not sure, if I got your idea... Do you think about something like this?
> 
 **snip**
> 
> class Bar(TFoo) if(is(TFoo : IFoo))
> {
> 	typeof(scoped!TFoo()) _foo;
> 	this()
> 	{
> 		_foo = scoped!TFoo();
> 	}
> }

Yes, pretty much.
Except if you want to pass a parameter to TFoo it'll become a mess.
And I expecially don't want it to become messy.
November 08, 2018
On Thursday, 8 November 2018 at 15:11:16 UTC, Sjoerd Nijboer wrote:
> Except if you want to pass a parameter to TFoo it'll become a mess.
> And I expecially don't want it to become messy.

I thought of this case... But passing the argument to TFoo directly while constructing Bar is messier, I think. With the solution above you could just:

´´´
import std.stdio;
import std.typecons;

void main()
{
	auto initData = 42;
	auto bar = new Bar!Foo(initData);
	assert(bar._foo.orWhatTypeEver == initData);
}

class Bar(TFoo) if(is(TFoo : IFoo))
{
	typeof(scoped!TFoo()) _foo;
	this(T)(T fooParam) //if(__traits(compiles, new TFoo(T.init)))
	{
		_foo = scoped!TFoo(fooParam);
	}
}

class Foo : IFoo
{
	int orWhatTypeEver;
	this(T)(T myParam)
	{
		orWhatTypeEver = /*cast(int)*/myParam;
	}
	void baz(){}
}
interface IFoo{ void baz(); }
´´´

In this case, Bar's constructor is not bound to any special type of input, letting Foo handle any input you want. Still, the solution is type safe.

And, maybe to calm my mind: Bar is the context of Foo, so it is allowed to see its inputs...
November 08, 2018
On Thu, 08 Nov 2018 12:45:57 +0000, Alex wrote:
> Hmm... not sure, if I got your idea... Do you think about something like this?

The point is dependency inversion. The class shouldn't need to know how to build its dependencies; it should leave that to other code. The fact that you can use the default constructor to build a thing is more coupling than desired.
November 08, 2018
On Thu, 08 Nov 2018 11:04:19 +0000, Sjoerd Nijboer wrote:
> I'm trying to invert the dependency from the classes `Bar -> Foo` to `Foo -> IFoo <- Bar` at compile time.
> 
> I do want `Foo's` to be embedded into `Bar`

These goals are a *little* at odds with each other; having a scoped!Foo puts significant constraints on how to build the object. But you know your needs a lot better than some generic advice.

I believe what you need to do is pass a factory function into the constructor. This is a bit awkward.

The really annoying part is that std.typecons doesn't have a named type for the scoped wrapper for a type. It's actively hostile to having scoped fields for no discernable reason. Filed https://issues.dlang.org/ show_bug.cgi?id=19379

Anyway, here's some code to make it work. It's kind of ugly.

---
import std.stdio;
import std.typecons;

void main()
{
	auto bar = new Bar!Foo((ref f) { f = scoped!Foo(); });
}

class Bar(TFoo) if(is(TFoo : IFoo))
{
	alias SFoo = typeof(scoped!TFoo());
	SFoo _foo;
	this(void delegate(ref SFoo) dg)
	{
		dg(_foo);
	}
}

class Foo : IFoo
{
	void baz(){}
}

interface IFoo
{
	void baz();
}
---
November 08, 2018
On Thursday, 8 November 2018 at 16:31:26 UTC, Neia Neutuladh wrote:
> I believe what you need to do is pass a factory function into the constructor. This is a bit awkward.

Yep, but I want a "nice and descriptive syntax" for it.

> Anyway, here's some code to make it work. It's kind of ugly.
>
> ---
> import std.stdio;
> import std.typecons;
>
> void main()
> {
> 	auto bar = new Bar!Foo((ref f) { f = scoped!Foo(); });
> }
>
> class Bar(TFoo) if(is(TFoo : IFoo))
> {
> 	alias SFoo = typeof(scoped!TFoo());
> 	SFoo _foo;
> 	this(void delegate(ref SFoo) dg)
> 	{
> 		dg(_foo);
> 	}
> }
>
> class Foo : IFoo
> {
> 	void baz(){}
> }
>
> interface IFoo
> {
> 	void baz();
> }
> ---

I tried to get something more nice looking and I'm stuck on the following.
I tried tom make a lazyscoped!T but I'm stuck at creating a constructor and determining the arguments from the Type. If I could do that I could just forward a lazyscoped!T which would have my parameters encapsulated to an object and have that object instantiate it at its place.
Maybe not the smartest idea but I think it's a little more descriptive than using a factory lambda.

```
import std.typecons : scoped;

void main() {
	auto initData = 42;
	auto bar = new Bar!Foo(lazyscoped!(Foo)(initData));
}

class Bar(TFoo, TArgs...) if(is(TFoo : IFoo)) {
	private typeof(scoped!TFoo())  _foo;
	
	this(lazyscoped!TFoo foo) {
		foo.construct(_foo);
	}
}

class Foo : IFoo {
	int orWhatTypeEver;
	this(T)(T myParam) {
		orWhatTypeEver = /*cast(int)*/myParam;
	}
	void baz(){}
}
interface IFoo{ void baz(); }

struct lazyscoped(T) {
	import std.typecons : scoped;
	TArgs args; // somehow determine TArgs assumimg there is only one __ctor of type T.
	
		this(TArgs args) {
			static if(TArgs.length > 0)
				this.args = args;
		}
		
	void construct(ref typeof(scoped!T()) scoped_t) {
		scoped_t = scoped!T(args);
	}
}```
November 09, 2018
On Thursday, 8 November 2018 at 21:16:32 UTC, Sjoerd Nijboer wrote:
> I tried tom make a lazyscoped!T but I'm stuck at creating a constructor and determining the arguments from the Type.

Unfortunately I can't find a way in D to get a list of arguments at compile time for a given function. Is this at all possible? It sounds like an easy case of introspection.
November 09, 2018
On Friday, 9 November 2018 at 09:13:56 UTC, Sjoerd Nijboer wrote:
> On Thursday, 8 November 2018 at 21:16:32 UTC, Sjoerd Nijboer wrote:
>> I tried tom make a lazyscoped!T but I'm stuck at creating a constructor and determining the arguments from the Type.
>
> Unfortunately I can't find a way in D to get a list of arguments at compile time for a given function. Is this at all possible? It sounds like an easy case of introspection.

Is it this what you are looking for?
https://dlang.org/phobos/std_traits.html#Parameters

November 09, 2018
On Friday, 9 November 2018 at 09:17:27 UTC, Alex wrote:
> Is it this what you are looking for?
> https://dlang.org/phobos/std_traits.html#Parameters
I've been looking over std.traits all day yesterday, how could I've missed that?
I'm so glad there are people in this forum that want to help out others like this. :)