Apply before merging option values

If I have the following option:

outputs = mkOption {
  type = with types;
    let outputTypes = oneOf [ str path attrs (functionTo attrs) ];
    in either outputTypes (listOf outputTypes);
  default = [ ];
  apply = outputs:
    toList (map (f:
      let e = silicon.import.should f;
      in iron.mif.null e.success e.value) outputs);
};

Is there any way for the apply function to be applied before the modules are merged? I’m trying to do something like the following:

mkMerge [
  { outputs = [ ... ]; }
  { outputs.name = "test"; }
]

But the second module isn’t being converted to a list before mkMerge is being applied.

If you’d like the actual code, let me know; it’s a tad long, so I didn’t want to overwhelm anyone right from the get go.

lib.mkBefore, lib.mkAfter, lib.mkOrder?

sorry, idk how these work yet, theres no docs LMAO

1 Like

Not sure that applies to the apply directive specifically! :sweat_smile:

1 Like

I’m not 100% clear on what you’re trying to achieve, but lib.types.coercedTo may be useful: NixOS Manual.

2 Likes

Yep. That seems to have done the trick. Thanks!

More generally you could use the merge function of mkOptionType, then use that type in the type option of mkOption. But yes, coercedTo would be the best option if there’s one type that you want everything to end up as.

1 Like

I’ll look into that as well!

It would appear I may have to use merge after all, as coercedTo doesn’t seem to allow type merging: types.coercedTo does not support type merging · Issue #352253 · NixOS/nixpkgs

That may be why I’m encountering an issue where outputs is converting functions into attribute sets of { __functionArgs = { ... }; __functor = ...; }.

Could you possibly give me a small example of how to use it?

EDIT: Either that or I don’t understand how to use lib.types.functionTo… How do I define an option that accepts functions, then?

Yeah that would be why.

Can you share an example of what you’d like the code to do? Let’s say I pass it a list containing an attrset and a function, like outputs = [ { a = 1; } (val: {a = val; }) ];, what should outputs contain at the end of this? (Or maybe you can explain what your usecase is and avoid x-y?)

1 Like

I basically want options to accept any of a string, a path, an attribute set, a function returning an attribute set, or a list of the previous types, all of which will finally be mkMerged into a single attribute set. It seems easy enough, but defining a function type seems to be a little more complicated than I anticipated…

I still don’t understand, how do you want to merge an attrset with a function?
If you can explain what you want to see with the example I gave above?

Sorry about that; got a bit confused myself. If a value passed to outputs is a function, is will be called with an inputs argument set, and should return an attribute set.

I kind of solved my problem by creating a isCoerced and isLambda function to check whether an outputs value is a function, so that I can put regular functors in my outputs if necessary, but I’m wondering if there’s a better way that will allow me to deal with these specific scenarios.