The string to number torture never stops

507 views
Skip to first unread message

Henrik Lindberg

unread,
Oct 31, 2014, 9:22:36 PM10/31/14
to puppe...@googlegroups.com
F.Zappa - excerpt from "The Torture Never Stops":

"Flies all green and buzzin'
In the dungeon of despair
Who are all those people
That he's locked away down there
Are they crazy
Are they sainted
Are they zeros someone painted
That had never been explained
Since at first it was created
But a dungeon like a sin
Requires not much lockin' in
Of everything thats ever been"

(http://www.metrolyrics.com/the-torture-never-stops-lyrics-frank-zappa.html
for full lyric)

Somehow fitting, Halloween and all... for what I want to bring up.

Yet again someone was bit by the automatic String to Numeric conversion
that is in Puppet (and also in --parser future).

The particular thing this time was
https://tickets.puppetlabs.com/browse/PUP-3602 that caused certain md5
fingerprints to look like floating point values of value 0 (they all
started with "0e" but had different digits after the e, and since 0 * 10
^something is always 0 the two different md5 fingerprints were reported
to being equal.

This is not the only place where String to number conversion is a very
bad idea. Someone recently had problems with conversions in a case
where string comparison was wanted for version strings.
https://tickets.puppetlabs.com/browse/PUP-3333 has the details.

With --parser future it is at least consistent. In 3x different
operators used different rules. Now <, >, <=, >=, ==, and != all share
the same behavior; they convert a string to numeric if both sides of the
expression are numeric or can be converted to numeric.

The in operator has some additional rules (see the spec, but it uses
the same comparisons as for the regular operators).

We fixed PUP-3602 by not converting strings that are floating point 0
with exponential part and we also do not convert values that are
floating point infinite in Ruby (e.g. 4e999 and such). This is a crutch
though, and it is only a matter of time until someone stumbles over the
next SURPRISE !

The best cure is naturally to never do String to numeric conversion. And
we wonder what people feel about that. Should we go for this in Puppet
4.0.0 (and have a 3.7.4 release as the last of the 3x series where this
behavior is implemented when using --parser future).


There has also been a number of other suggestions how to fix this, that
are less appealing, and for the sake of not having to waste anyone's
time, here they are, with the reasons why they are not so appealing.

* Only convert if LHS is a Number. This makes the order of the
comparison matter and then ($x == $y) != ($y == $x) which is really bad.

* Add === operator to compare both type and value. This is a slippery
slope since we probably want Integer and Float to compare equal - say
0.0 and 0. It adds yet another operator, and we have to decide what
case, selector and in should use since there is no way to specify if one
or the other should be used.

Instead, since we already have sprintf for value to string conversion,
and there is a scanf in Ruby which does the conversion the other way, we
can simply add that function to core puppet for value conversion.

We could also add to_number(s), or to_number(s, format_string).
When doing that though, we risk ending up with a plethora of to_xxx
functions, and we could instead offer one more universal
convert_to(Type, value, options) function e.g.

# best effort, or fail
convert_to(Number, value)

# only if it is an integer, or fail
convert_to(Integer, value, {base => 10})

# convert integer to hex string with some extra text (the sprintf way)
convert_to(String, value, {format => "Hex %x"})

# convert to array of string (give full control over all
# nested conversions.
#
convert_to(Array[String], value, {
Integer => { format => "0x%x" },
Float => { format => "%#.4G" }})

# etc.

So - "Boldly break all the (s)t(r)hings"?

- henrik

--

Visit my Blog "Puppet on the Edge"
http://puppet-on-the-edge.blogspot.se/

David Schmitt

unread,
Nov 1, 2014, 6:36:08 AM11/1/14
to puppe...@googlegroups.com
On 2014-11-01 01:22, Henrik Lindberg wrote:
> Somehow fitting, Halloween and all... for what I want to bring up.

Indeed. Cross type interactions are the worst.

> Yet again someone was bit by the automatic String to Numeric conversion
> that is in Puppet (and also in --parser future).
>
> The particular thing this time was
> https://tickets.puppetlabs.com/browse/PUP-3602 that caused certain md5
> fingerprints to look like floating point values of value 0 (they all
> started with "0e" but had different digits after the e, and since 0 * 10
> ^something is always 0 the two different md5 fingerprints were reported
> to being equal.

Wow.

> The best cure is naturally to never do String to numeric conversion. And
> we wonder what people feel about that. Should we go for this in Puppet
> 4.0.0 (and have a 3.7.4 release as the last of the 3x series where this
> behavior is implemented when using --parser future).

thirce yes!


The requirement to compare strings and numerals can also be implemented
by converting the numeral to a string (using a canoical non-locale
dependent format). This produces a much safer route, as creating a
string has less degrees of freedom.

> There has also been a number of other suggestions how to fix this, that
> are less appealing, and for the sake of not having to waste anyone's
> time, here they are, with the reasons why they are not so appealing.
>
> * Only convert if LHS is a Number. This makes the order of the
> comparison matter and then ($x == $y) != ($y == $x) which is really bad.
>
> * Add === operator to compare both type and value. This is a slippery
> slope since we probably want Integer and Float to compare equal - say
> 0.0 and 0. It adds yet another operator, and we have to decide what
> case, selector and in should use since there is no way to specify if one
> or the other should be used.

I'd not call that a slope, that's a cliff. See how PHP and javascript
struggle with this.

> Instead, since we already have sprintf for value to string conversion,
> and there is a scanf in Ruby which does the conversion the other way, we
> can simply add that function to core puppet for value conversion.
>
> We could also add to_number(s), or to_number(s, format_string).
> When doing that though, we risk ending up with a plethora of to_xxx
> functions, and we could instead offer one more universal
> convert_to(Type, value, options) function e.g.

Regardless of how you implement it, a good conversion function must be
*strict*. things like "3 Apples" (or md5 hashes) cannot safely be
converted into a number, even if some functions valiantly try.

>
> # best effort, or fail
> convert_to(Number, value)
>
> # only if it is an integer, or fail
> convert_to(Integer, value, {base => 10})
>
> # convert integer to hex string with some extra text (the sprintf way)
> convert_to(String, value, {format => "Hex %x"})
>
> # convert to array of string (give full control over all
> # nested conversions.
> #
> convert_to(Array[String], value, {
> Integer => { format => "0x%x" },
> Float => { format => "%#.4G" }})
>
> # etc.
>
> So - "Boldly break all the (s)t(r)hings"?
>
> - henrik
>


--
* Always looking for people I can help with awesome projects *
G+: https://plus.google.com/+DavidSchmitt
Blog: http://club.black.co.at/log/
LinkedIn: http://at.linkedin.com/in/davidschmitt

Kylo Ginsberg

unread,
Nov 1, 2014, 11:39:02 AM11/1/14
to puppe...@googlegroups.com
On Sat, Nov 1, 2014 at 3:35 AM, David Schmitt <da...@dasz.at> wrote:
On 2014-11-01 01:22, Henrik Lindberg wrote:
Somehow fitting, Halloween and all... for what I want to bring up.

Indeed. Cross type interactions are the worst.

Yet again someone was bit by the automatic String to Numeric conversion
that is in Puppet (and also in --parser future).

The particular thing this time was
https://tickets.puppetlabs.com/browse/PUP-3602 that caused certain md5
fingerprints to look like floating point values of value 0 (they all
started with "0e" but had different digits after the e, and since 0 * 10
^something is always 0 the two different md5 fingerprints were reported
to being equal.

Wow.

The best cure is naturally to never do String to numeric conversion. And
we wonder what people feel about that. Should we go for this in Puppet
4.0.0 (and have a 3.7.4 release as the last of the 3x series where this
behavior is implemented when using --parser future).

thirce yes!

+1. The less magic the better!
 


The requirement to compare strings and numerals can also be implemented by converting the numeral to a string (using a canoical non-locale dependent format). This produces a much safer route, as creating a string has less degrees of freedom.

There has also been a number of other suggestions how to fix this, that
are less appealing, and for the sake of not having to waste anyone's
time, here they are, with the reasons why they are not so appealing.

* Only convert if LHS is a Number. This makes the order of the
comparison matter and then ($x == $y) != ($y == $x) which is really bad.

* Add === operator to compare both type and value. This is a slippery
slope since we probably want Integer and Float to compare equal - say
0.0 and 0. It adds yet another operator, and we have to decide what
case, selector and in should use since there is no way to specify if one
or the other should be used.

I'd not call that a slope, that's a cliff. See how PHP and javascript struggle with this.

Instead, since we already have sprintf for value to string conversion,
and there is a scanf in Ruby which does the conversion the other way, we
can simply add that function to core puppet for value conversion.

We could also add to_number(s), or to_number(s, format_string).
When doing that though, we risk ending up with a plethora of to_xxx
functions, and we could instead offer one more universal
convert_to(Type, value, options) function e.g.

Regardless of how you implement it, a good conversion function must be *strict*. things like "3 Apples" (or md5 hashes) cannot safely be converted into a number, even if some functions valiantly try.

Agreed.

Re the many to_x functions vs one convert_to function, I lean toward the former approach because it's more aligned with the "do one thing well" design principle. Otoh I fear the single convert_to approach could end up harboring some odd corner cases and/or ambiguities (should a string of comma-delimited numbers convert to an array of Numbers?), which could be obviated by the clear, simple contracts of individual to_foo functions.

Kylo

 



# best effort, or fail
convert_to(Number, value)

# only if it is an integer, or fail
convert_to(Integer, value, {base => 10})

# convert integer to hex string with some extra text (the sprintf way)
convert_to(String, value, {format => "Hex %x"})

# convert to array of string (give full control over all
# nested conversions.
#
convert_to(Array[String], value, {
   Integer => { format => "0x%x" },
   Float   => { format => "%#.4G" }})

# etc.

So - "Boldly break all the (s)t(r)hings"?

- henrik



--
* Always looking for people I can help with awesome projects *
G+: https://plus.google.com/+DavidSchmitt
Blog: http://club.black.co.at/log/
LinkedIn: http://at.linkedin.com/in/davidschmitt


--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-dev+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-dev/5454B78C.5030202%40dasz.at.

For more options, visit https://groups.google.com/d/optout.



--
Kylo Ginsberg | ky...@puppetlabs.com | irc: kylo | twitter: @kylog

Join us at PuppetConf 2015, October 5-9 in Portland, OR - http://2015.puppetconf.com.  
Register early to save 40%!

Trevor Vaughan

unread,
Nov 1, 2014, 3:50:26 PM11/1/14
to puppe...@googlegroups.com
I was also bitten by this during testing and it made the thought of converting my code to 4.0 daunting.

So, I am very much not a fan of automatic String to Number conversion.

Thanks,

Trevor

To unsubscribe from this group and stop receiving emails from it, send an email to puppet-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-dev/CALsUZFGqmf5D5eWCrkMvjH5sKx8X3OFtk6uPvNz_OHycb4ktEg%40mail.gmail.com.

For more options, visit https://groups.google.com/d/optout.



--
Trevor Vaughan
Vice President, Onyx Point, Inc
(410) 541-6699
tvau...@onyxpoint.com

-- This account not approved for unencrypted proprietary information --

Daniele Sluijters

unread,
Nov 1, 2014, 5:18:32 PM11/1/14
to puppe...@googlegroups.com
I was also bitten by this during testing and it made the thought of converting my code to 4.0 daunting.

The auto conversion also holds for the current parser, so I don't see how that's making anything different, or daunting.

Essentially what it comes down to is input sanitisation. Up until a certain point in time there was no way to define that a value should be of a type. The language tried to be smart about it to work around the issue introducing a different scala of issues altogether.

Now that you can do that with the future parser there's no need for magic tricks that try to be intelligent about it. If we get two strings, we compare them as strings, even though they might also make sense as exponent numbers. And if a user goes 0 == 'orange' please don't do interpret that as 0 == 'orange'.to_i as that would return true but raise a Type or Runtime error instead.

If we want to fix this, now is pretty much the right moment to do it. Type juggling would need to become formally documented in the language spec, both the why and the how it does it so people can implement the same behaviour if they decide to write custom parsers/compilers. Once something as fundamental as this is in, it's going to be tough getting it out.

As a module author I want a language with the least amount of surprises and this behaviour is mighty surprising. You probably won't quickly run into it but when you do it'll make your head spin and take you a while to figure out what's going on.

As an ops person I want a predictable language where I can't accidentally subtract 0 from a potato and in return get spaghetti bolognese in my configuration file.

Now as far as the automatic conversion goes, or as PHP quite aptly calls it "type juggling", kill it. I'm always the one to go "break all the things" but really, this time, please "break all the strings".

Joshua Hoblitt

unread,
Nov 2, 2014, 4:19:17 AM11/2/14
to puppe...@googlegroups.com
On 11/01/2014 03:35 AM, David Schmitt wrote:
> On 2014-11-01 01:22, Henrik Lindberg wrote:
>
> The requirement to compare strings and numerals can also be
> implemented by converting the numeral to a string (using a canoical
> non-locale dependent format). This produces a much safer route, as
> creating a string has less degrees of freedom.
I believe this is the perl5 approach, which seems to be robust.
>>
>> * Add === operator to compare both type and value. This is a slippery
>> slope since we probably want Integer and Float to compare equal - say
>> 0.0 and 0. It adds yet another operator, and we have to decide what
>> case, selector and in should use since there is no way to specify if one
>> or the other should be used.
Oh hell no!
>>
>> # best effort, or fail
>> convert_to(Number, value)
It would require changes to the lexer but another syntactic option (not
that I'm advocating it) would be C style type coercion.

(String)$my_hungarian_integer

-Josh

--

Henrik Lindberg

unread,
Nov 2, 2014, 7:34:30 PM11/2/14
to puppe...@googlegroups.com
On 2014-02-11 10:19, Joshua Hoblitt wrote:
> On 11/01/2014 03:35 AM, David Schmitt wrote:
>> On 2014-11-01 01:22, Henrik Lindberg wrote:
>>
>> The requirement to compare strings and numerals can also be
>> implemented by converting the numeral to a string (using a canoical
>> non-locale dependent format). This produces a much safer route, as
>> creating a string has less degrees of freedom.
> I believe this is the perl5 approach, which seems to be robust.

One has to remember that Number to String also has a number of options -
which radix/base to use, the number of significant digits, scientific
notation for floating point etc.

>>>
>>> * Add === operator to compare both type and value. This is a slippery
>>> slope since we probably want Integer and Float to compare equal - say
>>> 0.0 and 0. It adds yet another operator, and we have to decide what
>>> case, selector and in should use since there is no way to specify if one
>>> or the other should be used.
> Oh hell no!
>>>
>>> # best effort, or fail
>>> convert_to(Number, value)
> It would require changes to the lexer but another syntactic option (not
> that I'm advocating it) would be C style type coercion.
>
> (String)$my_hungarian_integer
>
That is sort of what I am proposing - that there be one way to convert
by invoking an operation on a type.

> -Josh
>
> --

Thomas Hallgren

unread,
Nov 3, 2014, 4:09:47 AM11/3/14
to puppe...@googlegroups.com
On 2014-11-02 10:19, Joshua Hoblitt wrote:
>
>> # best effort, or fail
>> convert_to(Number, value)
> It would require changes to the lexer but another syntactic option (not
> that I'm advocating it) would be C style type coercion.
>
> (String)$my_hungarian_integer
I perceive this as a type cast, not a conversion, and would expect a runtime exception unless $my_hungarian_integer
already is a string.

- thomas

John Bollinger

unread,
Nov 3, 2014, 12:19:59 PM11/3/14
to puppe...@googlegroups.com


On Friday, October 31, 2014 8:22:36 PM UTC-5, henrik lindberg wrote:

[...]

Yet again someone was bit by the automatic String to Numeric conversion
that is in Puppet (and also in --parser future).



I must confess to a certain dark amusement at Puppet struggling with its weak-typing legacy and moving more and more in the direction of strong typing.  Not that I am in any way happy about these difficulties, but I'm an old-school dinosaur, and weak typing has never seemed like such a great idea to me.

[...]

We fixed PUP-3602 by not converting strings that are floating point 0
with exponential part and we also do not convert values that are
floating point infinite in Ruby (e.g. 4e999 and such). This is a crutch
though, and it is only a matter of time until someone stumbles over the
next SURPRISE !



Indeed so.  If you rely on heuristics to choose behavior then you have to accept that sometimes the wrong behavior will be chosen.  In this case, it is purely a guess that a string starting with "0e[digit]" is not meant to be interpreted as a number.

 
The best cure is naturally to never do String to numeric conversion.


What about numeric to string conversion?  I guess Puppet doesn't do that automatically now, so maybe this isn't the time to start, but that is an alternative approach to mixed string/number comparison.

 
And
we wonder what people feel about that. Should we go for this in Puppet
4.0.0 (and have a 3.7.4 release as the last of the 3x series where this
behavior is implemented when using --parser future).



I think avoiding automatic string to numeric conversion is consistent with forbidding bare strings starting with a digit.  It would be a lot better, though, if in the context of the manifest it were clearer which expressions are strings and which are numeric.  That's no problem for literals, of course, but none of this is interesting in the case where all the values involved are literals.  I (think I) understand that with the P4 parser and evaluator it will be possible to declare types specifically enough to address that issue, but it is also my understanding that expressions won't necessarily have formal types specific enough for that.

It is highly desirable to give manifest authors sufficient control over conversions to avoid unwanted ones, but it is not altogether clear to me whether the best approach is to nix all automatic string-to-number conversions.  A lot of existing manifests rely on such conversions, since they used to be the only alternative.  P4 is at liberty to break backward compatibility, but maybe a little less breakage would be wiser?



There has also been a number of other suggestions how to fix this, that
are less appealing, and for the sake of not having to waste anyone's
time, here they are, with the reasons why they are not so appealing.

* Only convert if LHS is a Number. This makes the order of the
comparison matter and then ($x == $y) != ($y == $x) which is really bad.



Yuck.

 
* Add === operator to compare both type and value. This is a slippery
slope since we probably want Integer and Float to compare equal - say
0.0 and 0. It adds yet another operator, and we have to decide what
case, selector and in should use since there is no way to specify if one
or the other should be used.



I agree that as proposed, the '===' operator would be troublesome.  There is always the alternative, though, of keeping '==' as it is, and making '===' simply perform a comparison without string/number conversion.  I think 'case', selector, and 'in' behavior are collectively a red herring, though: if '===' were adopted in any form then 'case', selector, and 'in' behavior would still be whatever is specified for them, whether that's their current behavior or a variant one based on '==='.  There is no requirement that that behavior be selectable between different senses of equality.

I'm not necessarily advocating that solution, but I think it's appropriate to take a careful, unbiased look at all the alternatives.  I'm not certain they're all on the table, yet.  For example, how about this:

* The value of an expression may be converted to a different type only to the extent that the target type is consistent with the expression's formal type.

For example, if a class parameter is declared to be type String then it's value cannot be automatically converted to Numeric or any of its subtypes, but if it is type Scalar or Object and happens to contain a string, then the string value can be automatically converted to a number (a Float, for instance).  That could yield backward compatibility for existing manifests that do not declare types, while still allowing authors to control the allowed conversions.

 
Instead, since we already have sprintf for value to string conversion,
and there is a scanf in Ruby which does the conversion the other way, we
can simply add that function to core puppet for value conversion.

We could also add to_number(s), or to_number(s, format_string).
When doing that though, we risk ending up with a plethora of to_xxx
functions, and we could instead offer one more universal
convert_to(Type, value, options) function e.g.

# best effort, or fail
convert_to(Number, value)

# only if it is an integer, or fail
convert_to(Integer, value, {base => 10})

# convert integer to hex string with some extra text (the sprintf way)
convert_to(String, value, {format => "Hex %x"})

# convert to array of string (give full control over all
# nested conversions.
#
convert_to(Array[String], value, {
   Integer => { format => "0x%x" },
   Float   => { format => "%#.4G" }})

# etc.



I do think there need to be type conversion facilities in some form.  I'm inclined to like the idea of a generic facility based on types, as opposed to a host of specific conversion functions.

 
So - "Boldly break all the (s)t(r)hings"?



I don't know how costly it would be to implement, but I rather like the combination of control, flexibility, and backward compatibility that could be afforded by using formal types to limit the scope of allowed conversions, instead of altogether forbidding (some) automatic conversions.


John

Joshua Hoblitt

unread,
Nov 3, 2014, 3:42:43 PM11/3/14
to puppe...@googlegroups.com
On 11/03/2014 02:09 AM, Thomas Hallgren wrote:
>>
>> (String)$my_hungarian_integer
> I perceive this as a type cast, not a conversion, and would expect a
> runtime exception unless $my_hungarian_integer already is a string.

Welcome to the "magic" of C. The type casting syntax is both a way of
explicitly declaring a conversion (primitives only) and a means to
bypass type safety. The behavior is dependent on the type.

https://en.wikipedia.org/wiki/Type_conversion_in_C

I was calling it out in conversion context.

-Josh

--

Joshua Hoblitt

unread,
Nov 3, 2014, 4:00:14 PM11/3/14
to puppe...@googlegroups.com
On 11/02/2014 05:34 PM, Henrik Lindberg wrote:
> On 2014-02-11 10:19, Joshua Hoblitt wrote:
>> On 11/01/2014 03:35 AM, David Schmitt wrote:
>>> On 2014-11-01 01:22, Henrik Lindberg wrote:
>>>
>>> The requirement to compare strings and numerals can also be
>>> implemented by converting the numeral to a string (using a canoical
>>> non-locale dependent format). This produces a much safer route, as
>>> creating a string has less degrees of freedom.
>> I believe this is the perl5 approach, which seems to be robust.
>
> One has to remember that Number to String also has a number of options -
> which radix/base to use, the number of significant digits, scientific
> notation for floating point etc.

All the languages that I'm aware of, if they do implicit conversion S
<-> I conversion, it's in base 10.

This is hex conversion in Ruby. It seems even if your explicit about
the conversion being hex you have to worry about case.

2.0.0-p353 :001 > "0F" == 15
=> false
2.0.0-p353 :002 > "0f".to_i(16) == 15
=> true
2.0.0-p353 :003 > "0F" == 15.to_s(16)
=> false
2.0.0-p353 :004 > "F" == 15.to_s(16)
=> false
2.0.0-p353 :005 > "f" == 15.to_s(16)
=> true
2.0.0-p353 :006 > "0F".to_i == 15
=> false
2.0.0-p353 :007 > "0F".to_i(16) == 15
=> true

That may be an argument for all conversions having to be explicit but
I'm fearful of the breakage that would cause for existing manifests,
assuming this wouldn't be an 'opt-in' behavior.

-Josh

--

Thomas Hallgren

unread,
Nov 3, 2014, 4:06:03 PM11/3/14
to puppe...@googlegroups.com
I know that primitives are converted by casts in may languages but most of them (if any) would not classify string as a primitive. I think we should refrain from this kind of "magic" since it's less clear what it will do. Anyone familiar with C++, C# or Java will probably perceive a string cast the same way I do.


-Josh

--

--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-dev+...@googlegroups.com.

Daniele Sluijters

unread,
Nov 3, 2014, 5:00:32 PM11/3/14
to puppe...@googlegroups.com
Agreed. This is a tool that 'ops' people should also be able to use and not require a black belt in C type casting or an understanding of the parser to figure out when a string is actually a string and will be treated as such.

Henrik Lindberg

unread,
Nov 4, 2014, 11:47:06 AM11/4/14
to puppe...@googlegroups.com
On 2014-03-11 18:19, John Bollinger wrote:
>
>
> On Friday, October 31, 2014 8:22:36 PM UTC-5, henrik lindberg wrote:
>
> [...]
>
> Yet again someone was bit by the automatic String to Numeric conversion
> that is in Puppet (and also in --parser future).
>
>
>
> I must confess to a certain dark amusement at Puppet struggling with its
> weak-typing legacy and moving more and more in the direction of strong
> typing. Not that I am in any way happy about these difficulties, but
> I'm an old-school dinosaur, and weak typing has never seemed like such a
> great idea to me.
>

It is never a great idea to have weak / no typing. It is an even worse
idea to have all types represented as strings...

> [...]
>
> We fixed PUP-3602 by not converting strings that are floating point 0
> with exponential part and we also do not convert values that are
> floating point infinite in Ruby (e.g. 4e999 and such). This is a crutch
> though, and it is only a matter of time until someone stumbles over the
> next SURPRISE !
>
>
>
> Indeed so. If you rely on heuristics to choose behavior then you have
> to accept that sometimes the wrong behavior will be chosen. In this
> case, it is purely a guess that a string starting with "0e[digit]" is
> not meant to be interpreted as a number.
>

Yes, this was a bit of a panic fix. It really sucks.

> The best cure is naturally to never do String to numeric conversion.
>
>
>
> What about numeric to string conversion? I guess Puppet doesn't do that
> automatically now, so maybe this isn't the time to start, but that /is/
> an alternative approach to mixed string/number comparison.
>
True, 4.0.0 will not do numeric to string automatically (because of
radix, precision and floating point formatting). I do not see that
coming back.

> And
> we wonder what people feel about that. Should we go for this in Puppet
> 4.0.0 (and have a 3.7.4 release as the last of the 3x series where this
> behavior is implemented when using --parser future).
>
>
>
> I think avoiding automatic string to numeric conversion is consistent
> with forbidding bare strings starting with a digit..

Good point.

> It would be a lot
> better, though, if in the context of the manifest it were clearer which
> expressions are strings and which are numeric. That's no problem for
> literals, of course, but none of this is interesting in the case where
> all the values involved are literals. I (think I) understand that with
> the P4 parser and evaluator it will be possible to declare types
> specifically enough to address that issue, but it is also my
> understanding that expressions won't /necessarily/ have formal types
> specific enough for that.
>

It is a bit difficult since operators are overloaded on type. The good
part is that if we stop transforming strings to numbers there will be
errors for arithmetic expressions.

The bad part is that ==, != cannot raise errors (since a string is
simply not equal to a number). Currently comparisons order all numbers
to be smaller than all strings. We could change those to instead error
if the types are not comparable to each other.

> It is highly desirable to give manifest authors sufficient control over
> conversions to avoid unwanted ones, but it is not altogether clear to me
> whether the best approach is to nix all automatic string-to-number
> conversions. A lot of existing manifests rely on such conversions,
> since they used to be the only alternative. P4 is at liberty to break
> backward compatibility, but maybe a little less breakage would be wiser?
>
While I am not so worried about the logic in the manifests themselves.
There has not been that many problems reported with respect to the
conversion in the other direction. For resource attributes however the
situation is worse since there are many types out there where it is
unknown how they deal with data types, what sort of munging / processing
they do of strings/numbers etc.

This would be the primary reason (IMO) to not do this until resource
types can type their parameters. (Since typed parameters direct
serialization, and there is no longer a question if a serialized "42"
should be a number or a string).

> * Add === operator to compare both type and value. This is a slippery
> slope since we probably want Integer and Float to compare equal - say
> 0.0 and 0. It adds yet another operator, and we have to decide what
> case, selector and in should use since there is no way to specify if
> one
> or the other should be used.
>
>
>
> I agree that as proposed, the '===' operator would be troublesome.
> There is always the alternative, though, of keeping '==' as it is, and
> making '===' simply perform a comparison without string/number
> conversion. I think 'case', selector, and 'in' behavior are
> collectively a red herring, though: if '===' were adopted in any form
> then 'case', selector, and 'in' behavior would still be whatever is
> specified for them, whether that's their current behavior or a variant
> one based on '==='. There is no requirement that that behavior be
> selectable between different senses of equality.
>
> I'm not necessarily advocating that solution, but I think it's
> appropriate to take a careful, unbiased look at all the alternatives.
> I'm not certain they're all on the table, yet. For example, how about this:
>
> * The value of an expression may be converted to a different type only
> to the extent that the target type is consistent with the expression's
> /formal/ type.
>
> For example, if a class parameter is declared to be type String then
> it's value cannot be automatically converted to Numeric or any of its
> subtypes, but if it is type Scalar or Object and happens to /contain/ a
> string, then the string value /can/ be automatically converted to a
> number (a Float, for instance). That could yield backward compatibility
> for existing manifests that do not declare types, while still allowing
> authors to control the allowed conversions.
>

Interesting idea, but problematic to implement and having good
performance. Now the type of an expression is encoded in the resulting
instance / value. Adding the more advanced "declared type narrows
conversion", I think it is required to also keep track of the declared
type of every (intermediate) value. All functions must declare their
return type etc. Since no functions do that now, we would basically
always operate on the Any type, and all conversions would be allowed.
In general I think narrowing the conversions to declared type would be
difficult. It could possibly be done when passing arguments to
functions, defined types, or parameterized classes. Currently it only
checks for type compliance, and I don't have any immediate ideas for how
to specify type conversion except something like specifying a lambda per
parameter to do type conversion, or special type conversion functions
that apply in different scopes. I think that adds complexity that is
more difficult to deal with than explicit conversion (i.e. accept a
broader type, then convert/assert inside the body of the construct).

Joshua Hoblitt

unread,
Nov 4, 2014, 12:28:50 PM11/4/14
to puppe...@googlegroups.com
On 11/04/2014 09:46 AM, Henrik Lindberg wrote:
> It is a bit difficult since operators are overloaded on type. The good
> part is that if we stop transforming strings to numbers there will be
> errors for arithmetic expressions.
>
> The bad part is that ==, != cannot raise errors (since a string is
> simply not equal to a number). Currently comparisons order all numbers
> to be smaller than all strings. We could change those to instead error
> if the types are not comparable to each other.

I think raising an exception when the types of operands to a comparison
operator are different is the least surprising behavior absent automatic
type conversion.

It would also provide at lot more confidence that a manifest is 4x safe
if it passes `puppet parser validate`. Without that you'd have to worry
about conditional expressions silently changing their result between 3x
and 4x.

-Josh

--

Trevor Vaughan

unread,
Nov 4, 2014, 12:33:42 PM11/4/14
to puppe...@googlegroups.com
Without that you'd have to worry about conditional expressions silently changing their result between 3x and 4x.

This x 1000. This is an absolute MUST for the release of 4.0 lest we silently alter every system out there (terrifying).

Trevor

--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-dev+...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Andy Parker

unread,
Nov 4, 2014, 3:04:06 PM11/4/14
to puppe...@googlegroups.com
The error would have to be at runtime, so puppet parser validate would not uncover these kinds of issues (except perhaps in trivial cases where it could be done). I have an experiment for creating a type inference system, but it is no where near complete enough to be a static type analysis system for puppet 4, and even then it would have to give up in far too many cases without yet more changes to the language.
 
-Josh

--

--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-dev/54590CCE.1030305%40cpan.org.
For more options, visit https://groups.google.com/d/optout.



--
Andrew Parker
Freenode: zaphod42
Twitter: @aparker42
Software Developer

Join us at PuppetConf 2015, October 5-9 in Portland, OR - http://2015.puppetconf.com 

Henrik Lindberg

unread,
Nov 4, 2014, 4:18:47 PM11/4/14
to puppe...@googlegroups.com
Remember though, that validation cannot catch results of functions and
expressions - since the validation is static, and we cannot really do
reasonable quality type inference (at least not yet).

- henrik

> -Josh
>
> --

Henrik Lindberg

unread,
Nov 4, 2014, 4:30:58 PM11/4/14
to puppe...@googlegroups.com
> So - "Boldly break all the (s)t(r)hings"?
>

Opened ticket PUP-3615 and we are going to make the change in 3.7.4 for
parser=future.

Joshua Hoblitt

unread,
Nov 4, 2014, 11:10:00 PM11/4/14
to puppe...@googlegroups.com
On 11/04/2014 01:04 PM, Andy Parker wrote:

It would also provide at lot more confidence that a manifest is 4x safe
if it passes `puppet parser validate`.  Without that you'd have to worry
about conditional expressions silently changing their result between 3x
and 4x.


The error would have to be at runtime, so puppet parser validate would not uncover these kinds of issues (except perhaps in trivial cases where it could be done). I have an experiment for creating a type inference system, but it is no where near complete enough to be a static type analysis system for puppet 4, and even then it would have to give up in far too many cases without yet more changes to the language.
Your absolutely right but  `puppet master --compile  localhost --manifest manifests/role/db.pp` would work.  Similarly, rspec-puppet tests would catch an exception from building the catalog even if there isn't coverage of the expression result.  Perhaps an option could be added to the parser face to eval expressions?

-Josh

--

Joshua Hoblitt

unread,
Nov 5, 2014, 9:45:28 AM11/5/14
to puppe...@googlegroups.com
Actually, I think I'm full of it.  It appears that although the master face accepts the --manifest, --modulepath, etc. flags in combination with --compile they don't have any visible effect on the generated catalog (my catalogs are so large it was hard to tell). In any event, the magic behavior I was hallucinating was parsing the supplied manifest and then evaluating it with cached facts (is --compile pulling facts out of inventory?).

Upon further consideration, there are significant problems with trying to implement a generic parse+evaluate checker that can be used in the same manner as `puppet parser validate` on arbitrary DSL code.   Defined but not declared classes / defined types may fail due to mandatory params, the use of evaluation order dependent functions (defined(), getparam(), etc.) and the `$caller_module_name != $module_name` idiom are all gotchas.

Still, I wonder if it's possible to use the parser to fish out the names of classes without mandatory parameters from of a series of manifests and automagically perform the equivalent of rspec-puppet `it { should compile }' or 'it { should compile.with_all_deps }`?  I suppose facter would either need to be invoked locally or an external corpus of facts would need to be supplied.  Hmm.

-Josh

--

Henrik Lindberg

unread,
Nov 5, 2014, 3:14:36 PM11/5/14
to puppe...@googlegroups.com
At cloudsmith we patched rspec-puppet to be able to feed in a set of
captured facts from a node. I think that is a feature that is missing in
rspec-puppet.

You can indeed to introspection of what is being parsed with the future
parser (the older parser is not completely side effect free and should
not be used for things like this). While not difficult, it does require
some coding to parse content and then find / filter the returned ast model.

You can also dump the parsed result as s-expressions (i.e. lisp like
tree) that shows the content of the ast model using puppet parser dump
Reply all
Reply to author
Forward
0 new messages