Add boolean in ParameterDescriptor to enforce parameters static typing#115
Add boolean in ParameterDescriptor to enforce parameters static typing#115
Conversation
|
ros2/rclcpp#1436 implements the above proposal except this point:
Though adding that is easy. |
clalancette
left a comment
There was a problem hiding this comment.
Along with the other code to implement this, I think this is fine.
My one hesitation here is that making this change means that Galactic will not be able to communicate with Foxy and earlier (at least, for ParameterDescriptor). We don't typically guarantee that, but it does happen to mostly work between Dashing, Eloquent, and Foxy. That shouldn't prevent this from going in, but it is something we should note in the release notes.
| bool read_only false | ||
|
|
||
| # If 'true', the parameter is not allowed to change its type. | ||
| bool static_typing true |
There was a problem hiding this comment.
Maybe:
| bool static_typing true | |
| bool statically_typed true |
Yeah, wire compatibility is hard in this case. |
I agree.
I agree with this, too. |
While I agree with this in principle, I think this will cause a lot of downstream headaches. For instance, I know that navigation2 does things like this: https://github.com/ros-planning/navigation2/blob/11b25a51981eb53db6a6a3601729ae85238d9241/nav2_dwb_controller/dwb_core/src/dwb_local_planner.cpp#L79 . That declares a parameter as NOT_SET by default, but expects the user to explicitly set this. I think that would run afoul of this limitation. So I think this is one we'll have to allow for pragmatic reasons.
I agree with this one. |
clalancette
left a comment
There was a problem hiding this comment.
Overall, I agree with the idea and changing this descriptor. The details to sort out in my mind:
- The name of the parameter (I like my suggested name slightly better)
- What to do about allowing NOT_SET -> conversion
- The actual implementation.
- Adding some documentation to the Galactic release notes about this semantic change.
That doesn't seem like a problem, as they are already "failing" when a default value is not provided. |
That sounds good to me.
Probably, it's worth creating a thread in ros discourse, though I would like first to have an implementation working with all the details sorted out. |
|
@jacobperron friendly ping, what are your thoughts about this? |
I'm a bit confused by this point. I think the concern is valid. The nav stack defaults several parameters to NOT_SET and then is checking that the use provides a value. So, for this type of use-case, users will not be able to use statically typed parameters (unless we allow having an initial NOT_SET state). If users were able to detect if a default value was overridden, then they could use that signal as a workaround. |
|
Perhaps it's okay to disallow statically typed parameters that default to NOT_SET, but I think we want to be sure in that choice, since it looks like changing our minds later will require another change to |
I see, yes they seem to be sometimes not passing a default value. IMHO, the use case we're missing is when you want a "statically typed" parameter with no default value. Something like: // throws if an override is not provided, or maybe the first `get()` does and `declare` doesn't throw.
node->declare_parameter(param_name, descriptor, rclcpp::ParameterType::Integer);or // throws if an override is not provided, or maybe the first `get()` does and `declare` doesn't throw.
descriptor.type = ParameterType::Integer;
node->declare_parameter(param_name, descriptor);We would need an |
|
I like the |
I was actually thinking of a single type (i.e.
Yes, we wouldn't need both. |
|
Maybe I'm wrong, but it seems like we would get a lot more flexibility for little effort in allowing multiple types. I think the API you described would look the same in the single allowed type case. E.g., only allow integers or allow integers and strings I'm okay if we don't support this, but seems like a nice way to further avoid user-defined parameter callbacks. |
|
This pull request has been mentioned on ROS Discourse. There might be relevant details there: https://discourse.ros.org/t/feedback-required-should-parameters-be-able-to-change-of-type/18319/1 |
Yes, I don't mind strongly. |
|
It also might help us avoid a future change to the message definition if we decided to support it later. |
What's the use case for a parameter that can have such vastly different types? |
Good point. I can't think of any specific cases. And I agree it would be bad design. I think I was under the wrong impression that people were currently using parameters with dynamic types (since that's the current default behavior). I change my mind and think a single "allowed type" is a good option. |
…nfigure if duck typing is desired or not Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
40804f9 to
f381146
Compare
@jacobperron do you have any preference between the currently proposed "static_typing" boolean and "allowed_type" field? I would like: node->declare_parameter("an_int", 5); // parameter_descriptor argument takes default valueto declare an integer parameter that cannot change of type. Moreover in this case: rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range = /*set integer range to something here*/;
node->declare_parameter("an_int", 5, descriptor);do you need to write The only problem is when no default value is provided, that as commented above that seems to be used currently.
The details are trivial to implement, but I'm not sure what is the preferred way forward ... |
|
A In the case where no parameter default value is provided, I'd rather not throw an exception since it seems like a valid use-case to me i.e., enforce that the user make a decision on a value instead of letting them use a default. I could be persuaded otherwise though. |
I'm not sure what that exactly means. |
|
I'm working on an alternative of the currently opened PRs, in order to experiment what's the best way to modify the behavior without breaking current use cases. I think we can discuss the final details in the review process |
I mean, I think it's a reasonable use-case to have a "required" parameter. In this case the parameter is not set, requiring the user to set it before it is used. Whether the type is set by the node author or inferred by the first override is another design decision that needs to be made. It would be nice for the node author to be able to specify the allowed type for an unset parameter, but I'm also okay with just inferring the type from the override to keep things simple. |
|
Closing in favor of #118. |
Currently parameters can change of type any time, which can be avoided by using a custom parameter callback (e.g. here).
When a parameter is changed to an unexpected type, it will usually generate an exception in the code using the parameter (see for example ros2/rclcpp#802), which is undesired.
What users commonly expect is that a parameter not change its type, so it would be good to provide an easy way to ensure that.
The parameter descriptor already contains a type field, but that is being used to reflect the last type that was set.
The proposal of this PR is to add a field called
static_typing, whentruea parameter cannot be changed of type.In that case, after the parameter is set to a type different than
PARAMETER_NOT_SET, that type is not allowed to change.Open questions:
static_typing=trueandtype=PARAMETER_NOT_SET?I would say no, an
intparameter should have an int default value provided when declared, and the user override (--ros-args -p ...) should match the type of the default value. i.e.:static_typing=true? IMO, no.Allowing that would actually make a parameter of type
intbehave as anoptional<int>, and it would still require special care when getting the parameter to avoid errors.Alternatives:
uint8 allowed_typefield in the parameter description, wherePARAMETER_NOT_SETwould allow dynamic typing. There's no big difference with the proposal above.typefield as theallowed_typefield described above.uint8[<=10] allowed_typesfield (we could use a boolean mask instead of an array). This approach is more flexible, but 99% of the time the user just wants to allow a single type, so not supporting this case doesn't sound important (and can still be achieved with dynamic typing and an user provided parameter callback).