#Field Assignment Constructors

1 messages · Page 1 of 1 (latest)

vernal girder
#

I currently work on a lightweight Dependency Injection framework. While I find it quite handy, I was unhappy with the boilerplate involved in writing constructors.

Inspired by features like Constructor Property Promotion in PHP/Hack and Parameter Properties in TypeScript, I've written something similar for D, except that it works the other way round.

Code sample

// regular way

class Service {
    private {
        Database db;
        Logger logger;
    }

    public this(
        Database db,
        Logger logger,
    ) {
        this.db = db;
        this.logger = logger;
    } 
}

becomes:

class Service {
    private @dependency {
        Database db;
        Logger logger;
    }
     mixin DependencyAssignmentConstructor;
}

Basically something also available in funkwerk-mobility's boilerplate but much more targeted at the DI use-case.

If you had to come up with a fancy name in the spirit of the PHP, TypeScript etc. counterparts, how would you name the concept applied here?

#

My naming ideas so far have been:

  • Field Assignment Constructors
  • Field Constructor Assignment Propagation

But I'm not sure whether I like them.

trail kindle
#

FWIW, I think it's better to mixin the body instead of the whole function. Yes, you have to repeat the member names, but a) this can bite you later if you add something -- all of a sudden your constructor changes, breaking any using code. and b) sometimes you want to make ctors that take just part of the fields, and assign others. I prefer something like: ```d
class Service {

public this(Database db, Logger logger) {
    mixin(assignMembersFromParameters);
}

}```

#

c) you might have private variables you don't want the user to be able to assign via ctor.

#

As far as a name for what you made, maybe defaultCtor?

#

or basicCtor?

vernal girder
trail kindle
#

Oh, I didn't notice the UDA!

#

But still, same thing applies: d class Service { private @dependency { Database db; Logger logger; Foo foo; // newly added } mixin DependencyAssignmentConstructor; } now existing code that just has new Service(db, logger) fails.

#

In addition, once you create a mixin template to add a constructor, you can't add any "normal" constructors. Better to use a string mixin.

vernal girder
vernal girder
# trail kindle But still, same thing applies: ```d class Service { private @dependency { ...

I think, practically, there are three cases:

  1. It doesn't matter. You've never called the ctor yourself, instead the DI framework does for you and it will just pick up on the change.
  2. The compilation error is as expected. You would have added a ctor parameter for the new field manually as well.
  3. You realize that your service class is no longer a proper service class; cohesion is suffering and it's time to refactor your code. Go to 1), go over start, draw $200.
trail kindle
#

I just mean, if you do: ```d
class A
{
mixin sometemplate;
this(int x) {}
}

vernal girder
#

Though I find it a bit worrying from the language side that the constructor is just ignored by the compiler when a hand-written one would have worked.

trail kindle
#

In this case, DI isn't really involved -- DI is deciding how to come up with a dependency based on the type of the parameter. What you are avoiding is member assignment boilerplate.

#

Basically, I still want to write the constructor, it might not just do member assignment.

vernal girder
#

For the DI use-case (that is what I'm targeting) you'll want to avoid all boilerplate. And you won't have more than one constructor – for convention over configuration reasons.

#

In other words: I wouldn't use the library for a type that doesn't go all in on no-boilerplate construction.

trail kindle
#

In Typescript for instance, I have a constructor like: ts constructor(private foo : Foo) { // do stuff with foo } and basically, the framework picks the dependency, but the language adds a member variable foo based on the ctor. But I still get to do stuff in the ctor.

#

The ctor body might do more than just boilerplate.

vernal girder
#

Unlike TypeScript or PHP, there's the option to have multiple constructors in D. That gives a whole different set of constraints, caveats and chances.

vernal girder
trail kindle
#

I suppose you could tag other functions to be called from the ctor, and write those but... it seems weird to make a new way to write constructors when D already has one.

#

It's possible to go "too far" on any kind of cool trick IMO.

vernal girder
vernal girder
trail kindle
#

You can get both by just splitting the impl from the decl

half iris
#

Maybe mixin DependencyAssignmentConstructor could be shortened a bit to mixin DependencyConstructor // DependenciesConstructor 😉

half iris