terraform-plugin-framework icon indicating copy to clipboard operation
terraform-plugin-framework copied to clipboard

`Length()` Function for Collection Types

Open zliang-akamai opened this issue 1 year ago • 5 comments

It's nice to have Length function for collection types (set, list, map, etc.), so we won't have to do len(list.Elements()) which is wasting time to allocate and release memory because Elements() make a new slice internally.

If you are willing to add this feature, I can try to put up a PR to implement it. Let me know.

zliang-akamai avatar Dec 13 '24 20:12 zliang-akamai

It's nice to have Length function for collection types (set, list, map, etc.), so we won't have to do len(list.Elements()) which is wasting time to allocate and release memory because Elements() make a new slice internally.

If you are willing to add this feature, I can try to put up a PR to implement it. Let me know.

@zliang-akamai: a PR would be welcomed.

bbasata avatar Jun 18 '25 16:06 bbasata

It might be worth thinking about what should happen if Length is called on a list whose ValueState is either null or unknown.

If Length were instead defined to return Int64Value then it could return an unknown int64 if the list is unknown, and a known Int64Value if the list is known. For a caller that wants to just ignore the possibility of unknown it would only be a little longer: list.Length().ValueInt64() (which would return zero in the unknown case), but this API would remind the author to consider whether they need to handle the unknown case.

Returning a null Int64Value when the list is null isn't quite so defensible, but it's at least consistent, and list.Length().ValueInt64() would still return zero in that case, which matches what len(list.Elements()) already does for a null list.

apparentlymart avatar Jul 16 '25 22:07 apparentlymart

It might be worth thinking about what should happen if Length is called on a list whose ValueState is either null or unknown.

If Length were instead defined to return Int64Value then it could return an unknown int64 if the list is unknown, and a known Int64Value if the list is known. For a caller that wants to just ignore the possibility of unknown it would only be a little longer: list.Length().ValueInt64() (which would return zero in the unknown case), but this API would remind the author to consider whether they need to handle the unknown case.

Returning a null Int64Value when the list is null isn't quite so defensible, but it's at least consistent, and list.Length().ValueInt64() would still return zero in that case, which matches what len(list.Elements()) already does for a null list.

I personally prefer returning int 0 for an unknown or null list because the size of the collection is not a Terraform config, plan, or state value. Additionally, I think we could add an options struct for handling null and unknown cases, similar to the one consumed by ObjectValue's As function.

However, I would definitely change my PR if the project host and/or the community thinks otherwise.

ChihweiLHBird avatar Jul 21 '25 19:07 ChihweiLHBird

(Sorry for the belated reply!)

The main hazard to think about when deciding between directly returning int or using a possibly-unknown Int64Value is the likelihood that provider logic relying on this function would end up accidentally producing a result that Terraform would consider to be invalid.

I'm having trouble thinking of a non-contrived example of this, and so perhaps it's not actually a big problem in practice, but here's a contrived example anyway to hopefully help explain what I mean: consider a hypothetical resource type that takes a list as an argument and then returns the length of that list in one of its computed attributes.

A naive implementation of that would just call Length, and then assign the result to the "length" attribute before returning.

However, if the input were an unknown list then that would set the "length" attribute to a known zero. If the list then ends up having a nonzero length once it's known, the apply-time logic would change "length" to a nonzero known value, which Terraform would then reject either as an "inconsistent final plan" or as an "inconsistent value after apply", depending on which of the two operations produced the now-invalid value.

To implement this correctly, the provider developer would need to check whether the given list is unknown and if so set the "length" attribute to be an unknown number, instead of zero. Terraform would then allow any number value as the final result, once the list becomes known in a later phase.

My suggestion was intended to give the provider developer a hint that there's a special case they need to handle here, so that they are more likely to write their implementation correctly the first time rather than learn about the problem only once someone tries to use an unknown list. If they aren't aware of this problem then they are unlikely to have thought to test for that situation before their first release.

Of course, how much this matters depends a lot on what a provider was going to use the length result for. The original issue didn't describe any specific reason why it would be important to know the length of a list, so I can't say whether that situation is one where handling unknown values or null values would be important.

apparentlymart avatar Nov 14 '25 19:11 apparentlymart

@apparentlymart Thanks for the thought! There is an existing pattern in the framework for handling a similar issue in ObjectValue's As function as I mentioned. If we adapt it a little bit for this Length case we can get:

someList.Length(basetypes.ListLengthOptions{
	UnhandledNullAsZero:    false, // panic if the list is null
	UnhandledUnknownAsZero: true, // return 0 if the list is unknown
})

So the plugin developer would know the expected results of the returned value.

Or maybe we can implement another function called LengthValue to return unknwon Int64 when the list is unknown, and clearly document the different between these 2 functions.

ChihweiLHBird avatar Nov 14 '25 21:11 ChihweiLHBird