Good morning, I wonder what are the conversion pat...
# dotnet
e
Good morning, I wonder what are the conversion paths between
Task
and
Task<T>
, and correspondingly do we want to model something similar to value-less
Task
in the
Output<T>
layer? I am going for
Output<Object>
carrying a
null
to correspond to “value-less” task. I really like how F# does it with
unit
type but that seems to not to be in .NET stdlib.
It sounds like more a .NET question then Pulumi question so let me contextualize 🙂
b
Task<T>
can implicitly be cast to
Task
but if you want to go the other direction you need to know what
TResult
is. Because you'd need to do something like
if (nonGenericTask is Task<T> genericTask)
Curious what use-case is for a value-less
Output<T>
e
I’m working on codegen changes to automatically lift functions to accept args as Input<.> and automatically promote results to Output<.>
Right so here I have a variation where the original function returns no value (it returns plain
Task
so caller can wait on it). And my lifted version is trying to lift that Task to Output.
Thinking about it, should I just return Task in this case from the lifted version… Hm…
b
I'm trying to follow - can you give me an overview of like what the method signature a consumer would use would look like and what they would expect to be returned? When you say "lifted version" which are you referring to
e
Does the gist help any? This function is taken from existing azure package: https://gist.github.com/t0yv0/a9139fd04f28a5d4e8f1cf20909023ae#file-codegen-cs-L17. I’m working to generate
Apply
version of the same function https://gist.github.com/t0yv0/a9139fd04f28a5d4e8f1cf20909023ae#file-codegen-cs-L22 - let me use
Apply
version instead of “lifted” sorry for misplaced jargon 🙂
The Apply version is the same thing as the original, except all parameters become Input. So, Input<string> instead of string, etc.
b
so is the goal to make the "get existing resource" functions easier to use?
e
Oh, right. It’s all sugar.
In the case of a function that used to return Task<Result>, the Apply version will generate Output<Result>, so far that sounds reasonable. Now, in the degenerate case, the function returns nothing (Task), and I’m pondering what to do for that kind of function in the Apply version.
b
Ah ok now I understand. In the cases where the function returns nothing is there a reason it couldn't just remain
Task
? Are consumers still passing something along from that such that it needs to be
Output
?
e
So, yes, the consumers are passing a long a bunch of
Input<T>
.
If we had a function available,
Task<T> AwaitInput<T>(Input<T> input)
b
ah, so we couldn't have a function that takes in N
Input<T>
but returns
Task
?
e
Then indeed we could return Task.
Let me puzzle it out a little more, maybe we still can.
b
I guess as far as usage goes it would be a little weird that you would
await
the functions that have no results but not the ones that do
What's an example of a one of these functions that doesn't return anything?
e
Well that’s a great question also, let me scan azure-native to see if there’s any such functions. Because we can always completely bail from generating these sugar helpers in complicated cases if that’s not useful.
Okay so sounds like we can return Task if we use side-effecting facilities:
Copy code
public static Task Apply(FuncWithConstInputApplyArgs? args = null, InvokeOptions? options = null)
        {
            var promise = new TaskCompletionSource();
            args.PlainInput.Apply(t => {
                var plainArgs = new FuncWithConstInputArgs
                {
                    PlainInput = t
                };
                InvokeAsync(plainArgs, options).ContinueWith(t => CopyTaskStateTo(t, promise));
                return t;
            });
            return promise.Task;
        }

        public static void CopyTaskStateTo(Task t, TaskCompletionSource promise) {
            if (t.IsCompletedSuccessfully)
            {
                promise.SetResult();
            }
            else if (t.IsFaulted)
            {
                promise.SetException(t.Exception);
            }
            else
            {
                promise.SetCanceled();
            }
        }
Basically create a promise, that eventually is linked to the Task returned from the function, and return right away. It seems reasonable to me - more reasonable than
Output<Object>
since users will be wondering what is that and what is the null in there.
Let me scan for actual examples to see how much this arises in practice.
No examples in pulumi-azure-native. Great
Checking AWS
Ok perfect! That is the right call here I believe, simply do nothing for the case of functions having no return values, as this case does not seem to arise in practice yet. Thanks a ton!
(no functions in AWS either like this)
b
it does look like that will work. Glad I could help
🙏 1