Description
I am stuck with this problem in #1371. Particularly, in that PR I am facing the decision of 1) use more features of traits, making this dependency stronger; 2) make the move to traitlets for good; 3) consider any option somebody else can provide.
IMHO, traits/traitlets are the way to go. They solve nicely the problem of defining the inputs and outputs of the interfaces. They are intuitive (from the user point of view) and creating nipype interfaces has been proven relatively easy. So, if there is a poll on this (keep traits vs. moving to something else), I'd stick with the traits.
Then, the question of traits vs. traitlets. In my opinion, we are underexploiting some features of traits:
- Features of both traits and traitlets we do not use:
- Events: trait(let)s are observable and using callbacks would ease our lives a lot. Some scarce uses have been done. But, generally, only for simple tasks like validating a field or even shadowing existing features of traits like synchronization.
- Synchronization: trait(let)s allow keeping traits synchronized
- Features only in traits that we do not use:
- Defining interfaces (somebody said interfaces?)
- Mapped traits: these are convenient traits in constructs like this, in which you can append an underscore to the trait name to get the mapped name (in this case using
interface.inputs.output_type_
would give you the current extension value, instead of the actual value of the trait. - Deferring and adaptation: I do not know why we would need this yet.
What we would get using these features:
- For instance, the implementation of the
IdentityInterface
would be trivial: when creating the input and output fields, we would just need to synchronize them (usinglink
in traitlets orsync
in traits). Synchronization can be directed (i.e. from intputs to outputs only) in both trait(let)s - Something that interacts with @satra 's advances on the new workflow design: connections between interfaces (should) could be also implemented very easily with synchronization.
- This would also make unnecessary some pieces of code that are making the interfaced workflow very tricky to implement. The logic under these
_propagate_...
functions is a bit cluttered and I have the impression that we just cover the mimimum set of situations. - Keeping the workflow synchronized with traits would remove this task from ourselves, and the pipeline engine will have two responsibilities: ordering the tasks and run them in that order. Which is a lot simpler.
- Using traits.Interface makes the base code of interfaces a lot more readable and simple. In [WIP] Major refactor (nipype-1.5 ?) #1371, I proposed that interfaces, inputs and outputs are traits.Interfaces.
- This would also make unnecessary some pieces of code that are making the interfaced workflow very tricky to implement. The logic under these
I am probably forgetting a lot of stuff, but I hope this is more than enough to make an informed decision. What are your thoughts?