Some keys like "shortName" are strings (native type "string") and it doesn't make any sense to set them as an integer or float as they are not possible values for the key.

Some keys are native type integer like "perturbationNumber" and you can set/get them as integer, float or string as you like.

Some keys are codetables and for them you have a code (integer) and an abbreviation (string) like for example "centre". You can set them as a string or as an integer, not as a float because there is no mapping for those keys into a float. An example is:

% grib_set -s centre:s=ecmf  in.grib out.grib  # Set the key 'centre' as a string
% grib_set -s centre:i=98    in.grib out.grib  # Set the key 'centre' as an integer

Some keys are concepts and they are usually strings like stepType, gridType, packingType. Again setting them as integer doesn't make any sense as they don't have any mapping as integers.

A concept is a key with a special association with other keys. Let's take shortName as an example. Its definition in the .def files is made with a long list of entries like:

'cape' = {
     discipline = 0 ;
     parameterCategory = 7 ;
     parameterNumber = 6 ;
     typeOfFirstFixedSurface = 1 ;
     typeOfSecondFixedSurface = 8 ;
}

this means that if you set shortName="cape" (as a string), ecCodes will automatically set the keys listed in curly brackets to the corresponding values.

Conversely when you get shortName, ecCodes looks for the best match of the keys listed in the full definition of shortName (not only cape) and will return "cape" only if the following are true:

  discipline == 0
  parameterCategory == 7
  parameterNumber == 6
  typeOfFirstFixedSurface == 1
  typeOfSecondFixedSurface == 8

and there isn't a better match of keys in the list.

Again saying shortName=123 doesn't make any sense as in the context of shortName we don't need numbers.

The idea is that the user should know what to set before choosing the type to use. In the sense that the user should know the meaning of the key before setting it to a value.

There was a bit of confusion regarding the type of step and paramId for the following reason:

step

In the past step was a string because you can have values like '24-36' which you cannot represent as a number. In MARS the step is only the endStep and therefore we had to modify ecCodes to be MARS compliant. Now the step is an integer and set to endStep. As we need a step indicating a range we introduced stepRange which is a string and cannot be set as an integer and will never be set as an integer because it allows values like stepRange=24-36. It is true that most of the time you have stepRange=36 or stepRange=24 or another single number, but this is because in those cases you have also stepType=instant and endStep=startStep and we don't want to write stepRange=24-24 which is redundant. We also write stepRange=24 when stepRange=0-24 (MARS compatibility). If you prefer to set the step as an integer instead of using a string you have the keys starStep and endStep which are integers and they can also be set as string because it is possible to convert for example the number 24 into the string "24". Therefore you can do:

% grib_set -s endStep=24 in out
% grib_set -s endStep:s=24 in out

paramId

It is a concept like shortName and unfortunately in the previous version the native type for a concept was string even if the meaning of it is number. In the new version paramId is still a concept, but it has native type number. This means that it can be set as a number, but cannot be set as a string because only the numbers are valid values for paramId. You can still get it as a string because it is possible to convert a number into a string. You cannot set it as a string because it isn't always possible to convert a string into a number.