You think you're an X, but you're only a Y
The other day I was converting the output of a Git::Raw::Commit into JSON using my module JSON::Create, when I noticed an oddity:
{
"commits":[
{
"body":null,
"id":"27ed4669e32ce2d14831c719dfd5b341a659788e",
"message":"Remove a stray html ending tag\n",
"time":"1609997818"
},
The "time" part always comes out as a string, even though it's clearly a number. Was this a bug in my module, some kind of dual-string-and-number wannabee variable which JSON::Create falsely turned into a string?
As it happens, no. Git::Raw actually puts the number into a string. (The newSVpv there makes a new Perl string, and the sprintf above that does exactly the same job as Perl's sprintf.)
So Git::Raw turns the original C variable of the form git_time_t
, a 64-bit integer type representing the number of seconds since the "epoch" (1970), into a string, perhaps to avoid the "year two million" problem or whatever, because Perl can hold up to 52 or 53 bit integers.
Anyway Perl's monkey business with numbers and strings, and the lack of booleans, makes creating JSON quite complicated, although not as complicated as identifying cats in photographs and youtube videos.
I thought that as well when I had to deal with stricter consumers of my JSON but then, rooting through the Cpanel::JSON::XS docs and just reading into perl variables I just saw that for any number you can do latitude => $lat + 0 or have real booleans with
{
some_bool => $var ? \1 : \0,
}
But yes of course it's a bit of a pain if you dont have control over that..
Most people seem to end up using "looks_like_number" in Scalar::Util for these kinds of jobs.
https://perldoc.perl.org/Scalar::Util#looks_like_number
Apparently it's based on an internal function that Perl uses to distinguish the two:
https://perldoc.perl.org/perlapi#looks_like_number
I suppose I could use that in the JSON generator but for the most part it would slow things down a lot for only a tiny benefit. In my case I'm sending the "number" from Perl to Go, so the fact that the time is a string in Perl means it has to be a string in the Go struct as well. That's also why it's slightly annoying in this case.
Generally I use the "boolean" module from CPAN if I want to force things:
https://metacpan.org/release/boolean
It's pretty effective.
Probably some Perl person will tell you there is some dreadful thing or another wrong with this module, since that always seems to be the case with everything about Perl.
Anyway I hope Perl 7 will have booleans.
Ironically the Perl source code is littered with macros "TRUE" and "FALSE" but they weren't allowed in the language itself.