A proposal to add String.prototype.format

Shanjian Li shanjian at google.com
Wed Mar 9 13:36:49 PST 2011


I like this idea.  I thought a lot about how to support those locale
specific stuff like plural and gender. Your suggestion provide an elegant
way to transfer the responsibility to a more appropriate party.


shanjian

On Wed, Mar 9, 2011 at 1:18 PM, Bob Nystrom <rnystrom at google.com> wrote:

>  It doesn't specify how to print objects, except for %s, which says that if
>>> the argument is not
>>> a string, convert it to string using .toString().
>>>
>>
>> If the format specifier does not apply to the argument given, it should
>> raise exceptions. Except string conversion, no other conversion will be
>> done.
>>
>
> I like your first six points, but the formatting string stuff feels odd to
> me: neither dynamic nor object-oriented. C needs format specifiers because
> it doesn't otherwise know now to interpret the bits you give. ES doesn't
> have that problem.
>
> At the same time, baking a rigid set of formatting instructions into
> string.format() feels like a poor separation of concerns. string.format()'s
> just is to compose a string out of smaller pieces. Why should it need to
> know anything about numbers or dates?
>
> Could we just say that this:
>
>     "hi, {0:blah}.".format(someObj);
>
> Is (conceptually) desugared to:
>
>     ("hi, " + someObj.toFormat("blah") + ".")
>
> So anything after the ":" in an argument (the format string) gets passed to
> the object itself by way of a call to toFormat() (or some other method
> name) on it. Then each object can decide what format strings are appropriate
> for it.
>
> This keeps the responsibilities separate: string.format() does
> composition, and the composed object own their own formatting. It's also
> extensible: you can define your own formatting capabilities for your types
> and use them with string.format() by defining toFormat(). (By default, I
> would assume that Object.prototype.toFormat() just calls toString()).
>
> This, I think, helps with locale issues too. Types like Date that care
> about locale will be able to handle it themselves in their call to
> toFormat() without string.format() needing to deal with it.
>
> - bob
>
>
>>
>>
>>> The string conversion should probably use the internal ToString function
>>> instead (which works for null
>>> and undefined too).
>>>
>>
>> Agree.
>>
>>
>>> For formats expecting numbers, it should convert the argument to a number
>>> using ToNumber.
>>>
>>
>> Probably not. As string is the thing being constructed, it make sense to
>> offer "hidden" string conversion. In my experience using this feature in
>> Python, it is within expectation and offer some convenience. Any further
>> "hidden" conversion should really be avoided.
>>
>>
>>>
>>> Rounding is specified as "math.round(n - 0.5)" (capital M in Math?).
>>>
>>
>> Right, thanks.
>>
>>
>>> This leaves it open whether overwriting Math.round should change the
>>> behavior of format. It probably
>>> shouldn't (i.e., again it would be better to specify in terms of
>>> internal, non-use-modifiable functions).
>>>
>>
>> Agree.
>>
>>
>>> The rounding is equivalent to Math.floor(n) (aka round towards
>>> -Infinity), if I'm not mistaken, so why
>>> not just use that?
>>>
>>
>> In this example, 8 / (3 - 8 / 3) , the display will be 23.99999999999999.
>> So the internal representation could be a little bit more or a little bit
>> less than the theoretical value due to float precision. Math.round might
>> generate less surprise results than Math.floor.  Of cause, the internal
>> implementation shouldn't rely on either of these two.
>>
>>
>>
>>> (Personally I would prefer truncation (round towards zero), if conversion
>>> to integer is necessary).
>>>
>>> Why can octal, binary and hexidecimal forms only be used on integers?
>>> Number.prototype.toString with
>>> an argument works on fractions too (try Math.PI.toString(13) for laughs
>>> :).
>>>
>>
>>
>>> Why only fixed bases (2,8,10,16)? How about adding an optional base
>>> parameter to number display (with
>>> x, d, o, b as shorthands for the more standard bases)? Again,
>>> Number.prototype.toString means that it's
>>> already in the language. (I know that step 7 says copy the format of
>>> other languages, but that seems
>>> shortsighted since ECMAScript is not those languages, and only copying
>>> functionality from C verbatim
>>> seems like tying your shoelaces together before the race).
>>>
>>> The question for both questions is how useful is that. If it is only
>> needed in one or few rare occasions, it is probably not a good idea to
>> complicate the language.
>>
>>
>>
>>>
>>> "Placeholder used in format specifier part can not have format specifier.
>>> This prevent the replacement
>>> from embedding more than one level."
>>> Should that be "... can not have a placeholder."?
>>>
>> No.   The former prevent any format specifier (including embedded
>> placeholder). Refer to the Python specification, it does make sense.
>>
>>
>>
>>> If the placeholder value is not a string, it should be converted to a
>>> string.
>>> If it is not a valid format, what happens then?
>>>
>>
>> Raise exception?
>>
>>
>>>
>>>
>>> Is the following valid:
>>>  "{x} and {1[y]}".format({x:42},{y:37})
>>> I.e., can object property shorthands ({x} instead of {0[x]}) be used if
>>> there are more than one argument?
>>>
>>
>> Good points. Possible choices:
>> 1. {x} always refer to the first object given.
>> 2. {x} only works when there is one and only one object argument.
>> 3. {x} will be replaced by the first object that has property x, ie. the
>> following should work too.
>>     "{x}, {z} and {1[y]}".format({x:42}, {z:43, y:37})
>>
>> I prefer 1.
>>
>>
>>>
>>>
>>> And some arbitrary ideas for extension:
>>>
>>> How about a boolean test that checks for falsy-ness of the argument and
>>> acts as one of two other
>>> formats or literals?
>>> E.g.
>>>  "{0:s} drew {1:?his|her} gun.".format(person.name, person.isMale)
>>>  "Please press return{0:?.|{1}}".format(notCritical, " and run!")
>>>
>>
>> Interesting. In example 1, the issue is literal get into the placeholder,
>> that could make things messy.
>>
>>
>>
>>>
>>>
>>> Or allow computed indices?
>>>  "{0[{1}][he]} drew {0[{1}][his]} gun.".format({male:{he:"He",his:"his"},
>>> female:{he:"She",his:"her"}}, "female");
>>>
>>
>> Allow embedded placeholder inside the field part (not the format specifier
>> part) of a placeholder is something that I will be very cautious about.
>>
>> shanjian
>>
>>
>>>
>>>
>>> /L
>>> --
>>> Lasse Reichstein - reichsteinatwork at gmail.com
>>>
>>
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110309/93841505/attachment-0001.html>


More information about the es-discuss mailing list