
(Sorry for the crosspost; I'm not sure which list this should go to.) I've just completed a pure-Haskell printf. Docs at [1], download at [2]. Here are some examples: vsprintf "Hello"
"Hello" vsprintf "Hello, %s\n" "John" "Hello, John\n" vsprintf "%s, your age is %d\n" "John" (10::Integer) "John, your age is 10\n"
sprintfAL "%(name)s, your age is %(age)d\n" [("name", v "John"), ("age", v (10::Integer))]
"John, your age is 10\n"
I have more examples available at the doc page[1]. I used Ian Lynagh's Printf.Printer module to do the actual formatting (I converted it away from TH first). I got the idea for simulating handling variable numbers of function arguments from the haskell-xml-rpc code. I would appreciate comments/critiques. -- John [1] http://gopher.quux.org:70/devel/missingh/html/MissingH.Printf.html [2] http://gopher.quux.org:70/devel/missingh

On Mon, 15 Nov 2004, John Goerzen wrote:
Here are some examples:
vsprintf "Hello"
"Hello" vsprintf "Hello, %s\n" "John" "Hello, John\n" vsprintf "%s, your age is %d\n" "John" (10::Integer) "John, your age is 10\n"
sprintfAL "%(name)s, your age is %(age)d\n" [("name", v "John"), ("age", v (10::Integer))]
"John, your age is 10\n"
I would appreciate comments/critiques.
Variable length argument lists are really a mess. Why are people so keen on them? What is the advantage over a plain list as single argument? Is vsprintf "%s, your age is %s\n" ["John", show (10::Integer)] really too complicated?

Henning Thielemann writes:
Variable length argument lists are really a mess. Why are people so keen on them?
One advantage is that you need to type fewer characters. It's, well, not _that_ important, I'll readily admit. :-) But vsnprintf "i = %d;\tj = %s" 12 "test" is more compact than any other notation I'd be aware of. And it comes, of course, at the price of being rather dangerous, depending on what you do. Personally, I have found ShowS style text-formatting to be my favorite. The code verb = showString msg = (verb "i = ") . (12 `shows`) . (verb "\tj = ") $ "test" isn't mind-blowingly compact either, but I always feel an odd satisfaction when writing this, because it's supposedly very efficient -- which justifies the extra characters, IMHO. :-) Peter

On 16 Nov 2004, Peter Simons wrote:
Henning Thielemann writes:
Variable length argument lists are really a mess. Why are people so keen on them?
One advantage is that you need to type fewer characters.
I know memory is expensive, that's why only the last two digits of year numbers are stored. :-]
It's, well, not _that_ important, I'll readily admit. :-)
I'm afraid, that's the only reason. :-(
Personally, I have found ShowS style text-formatting to be my favorite. The code
verb = showString msg = (verb "i = ") . (12 `shows`) . (verb "\tj = ") $ "test"
isn't mind-blowingly compact either, but I always feel an odd satisfaction when writing this, because it's supposedly very efficient -- which justifies the extra characters,
You can save even more characters: msg = verb "i = " . shows 12 . verb "\tj = " $ "test"

Henning Thielemann writes:
One advantage is that you need to type fewer characters.
I know memory is expensive, that's why only the last two digits of year numbers are stored. :-]
I understand what you're getting at -- and I find it annoying, too, when people sacrifice robustness for comfort. I'm not sure, though, whether this is the case here, because vsnprintf in Haskell still is type-safe. You'll get more complicated error messages, the memory footprint might be worse, but it still _is_ robust code. Perhaps it really is a matter of personal taste. Just for the sake of seeing the point from all possible perspectives, I could think of another reason why you might need a function like that: If you want to provide printf-style variable substitutions to the user, say in a config file. People are _used_ to this mechanism, and many programs I know offer this feature to customize text templates, etc. It can't hurt to have a function that does the parse job and returns the result for you. Although, if you think this through, you'll soon arrive at the conclusion that for this particular use-case hard-coded place-holders (like %s, %d, etc.) are not that useful. You'd like to be able to (easily!) extend the function, to offer a more general variable substitution, like sh(1) does. So that you could write Dear ${customer}, are you interested in making ${phantasy-number} fast? and pass a function (String -> a) to vsnprintf which does the lookup. I'm not sure how having different types of values in the dictionary plays into this, though.
You can save even more characters:
msg = verb "i = " . shows 12 . verb "\tj = " $ "test"
Right! One more reason to use ShowS-style. :-) Peter

At the risk of getting off topic... the reason 'C' has printf is because it is not polymorphic. Printf is a hack to allow different types to be printed out, such that they did not need printInt, printFloat etc. Remember C is typesafe, so the only way they could do this was to pass the first argument (the format string) to extract the types of the following arguments. Haskell doesn't need such tricks as it is polymorphic, we can just use 'show' no matter what the type is. Keean. Peter Simons wrote:
Henning Thielemann writes:
One advantage is that you need to type fewer characters.
I know memory is expensive, that's why only the last two digits of year numbers are stored. :-]
I understand what you're getting at -- and I find it annoying, too, when people sacrifice robustness for comfort.
I'm not sure, though, whether this is the case here, because vsnprintf in Haskell still is type-safe. You'll get more complicated error messages, the memory footprint might be worse, but it still _is_ robust code.
Perhaps it really is a matter of personal taste.
Just for the sake of seeing the point from all possible perspectives, I could think of another reason why you might need a function like that: If you want to provide printf-style variable substitutions to the user, say in a config file. People are _used_ to this mechanism, and many programs I know offer this feature to customize text templates, etc. It can't hurt to have a function that does the parse job and returns the result for you.
Although, if you think this through, you'll soon arrive at the conclusion that for this particular use-case hard-coded place-holders (like %s, %d, etc.) are not that useful. You'd like to be able to (easily!) extend the function, to offer a more general variable substitution, like sh(1) does. So that you could write
Dear ${customer},
are you interested in making ${phantasy-number} fast?
and pass a function (String -> a) to vsnprintf which does the lookup. I'm not sure how having different types of values in the dictionary plays into this, though.
You can save even more characters:
msg = verb "i = " . shows 12 . verb "\tj = " $ "test"
Right! One more reason to use ShowS-style. :-)
Peter
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Andreas Rossberg wrote:
Keean Schupke wrote:
Remember C is typesafe
In which parallel universe?
I of course meant strongly-typed, you cannot pass a pointer to an int where a pointer to a float is required ... modern C compilers require you to explicitly cast. Where it fell down was all that automatic type promotion, and providing unsafe casts. There is now a typesafe 'C', but I can't remember what it is called - presumably it uses some kind of linear-alias typing to make pointers safe. Keean.

Keean Schupke wrote:
I of course meant strongly-typed, you cannot pass a pointer to an int where a pointer to a float is required ... modern C compilers require you to explicitly cast.
According to the C standard, void f(float *p) { *p + 1.0; } void g(void *p) { f(p); } void h(int n) { g(&n); } is perfectly valid (though undefined).
Where it fell down was all that automatic type promotion, and providing unsafe casts.
And other things, like unions, varargs, etc. Not to speak of subtyping in C++...
There is now a typesafe 'C', but I can't remember what it is called - presumably it uses some kind of linear-alias typing to make pointers safe.
Do you mean Cyclone? http://www.research.att.com/projects/cyclone/ Cheers, - Andreas -- Andreas Rossberg, rossberg@ps.uni-sb.de Let's get rid of those possible thingies! -- TB

Keean Schupke wrote:
At the risk of getting off topic... the reason 'C' has printf is because it is not polymorphic. Printf is a hack to allow different types to be printed out, such that they did not need printInt, printFloat etc.
Many language have printf-like functions despite not satisfying this criterion. Perl, Python, and Common Lisp are the three that come to mind. I think the reason they have it is that it's useful in general to be able to visually separate the string template from the expressions being printed. Even (name++", your age is"++age++".") is pretty punctuation-heavy for a template. (Plus, it has a bug in it, which would be much easier to see in the printf syntax.) -- Ben

Ben Rudiak-Gould wrote:
Keean Schupke wrote:
At the risk of getting off topic... the reason 'C' has printf is because it is not polymorphic. Printf is a hack to allow different types to be printed out, such that they did not need printInt, printFloat etc.
Many language have printf-like functions despite not satisfying this criterion. Perl, Python, and Common Lisp are the three that come to mind.
I think the reason they have it is that it's useful in general to be able to visually separate the string template from the expressions being printed. Even (name++", your age is"++age++".") is pretty punctuation-heavy for a template. (Plus, it has a bug in it, which would be much easier to see in the printf syntax.)
Perl, as I see it, has printf for two reasons. The first is because sometimes printf "something%dsomething else", $number; is nicer to deal with than print "something${number}something else"; but I think primarily because printf lets you specify the number formatting and other such things, which Perl's variable interpolation system won't. Things like printf "%02d", $number; Are invaluable at times. That, as I see it, is the value of printf-like functions. It's certainly preferable (no matter how painful to the parts of us which like nice clean pure code) to C++'s rather unpleasant iostreams system of throwing random objects down a stream to change the number formatting pattern. Ugh.

On 2004-11-16 at 11:42+0100 Peter Simons wrote:
Henning Thielemann writes:
One advantage is that you need to type fewer characters.
I know memory is expensive, that's why only the last two digits of year numbers are stored. :-]
I understand what you're getting at -- and I find it annoying, too, when people sacrifice robustness for comfort.
I'm not sure, though, whether this is the case here, because vsnprintf in Haskell still is type-safe.
Not statically, though, surely?
vsprintf "%d, your age is %s\n" "John" (10::Integer)
is type incorrect, but won't be reported at compile time. At least I can't see how it could be, given that the string can't be dissected at compile time.
You can save even more characters:
msg = verb "i = " . shows 12 . verb "\tj = " $ "test"
Right! One more reason to use ShowS-style. :-)
and that really is type safe. -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk

On 2004-11-16, Jon Fairbairn
On 2004-11-16 at 11:42+0100 Peter Simons wrote:
I'm not sure, though, whether this is the case here, because vsnprintf in Haskell still is type-safe.
Not statically, though, surely?
vsprintf "%d, your age is %s\n" "John" (10::Integer)
is type incorrect, but won't be reported at compile time. At least I can't see how it could be, given that the string can't be dissected at compile time.
You are correct. One of the drawbacks of doing this in pure Haskell is that you lose compile-time type checking. The above code will throw an exception at runtime. Ian Lynagh's Template Haskell-based Printf doesn't suffer from that problem, since it can generate the appropriate Haskell code at compile time. On the other hand, it is less portable. So not all printf implementations for Haskell have this problem. So, people have an option. That's my intent. -- John

On 16 Nov 2004, Peter Simons wrote:
I'm not sure, though, whether this is the case here, because vsnprintf in Haskell still is type-safe. You'll get more complicated error messages, the memory footprint might be worse, but it still _is_ robust code.
Yes and no. It can't be checked statically if the number of placeholders matches the number of arguments. It can't be checked statically if the types of placeholders match the types of arguments. It is not possible to create functions with more than one variable length parameter lists.
Dear ${customer},
are you interested in making ${phantasy-number} fast?
and pass a function (String -> a) to vsnprintf which does the lookup. I'm not sure how having different types of values in the dictionary plays into this, though.
The function MissingH.Printf.sprintf is probably the better choice, but one could even replace [Value] by [String]. The conversion from any type to String can be easily done using 'show' by the caller. Though it gives the author of the format string less control over the formatting of particular types like numbers.

Actually it can be statically checked, as the string is a constant, we can lift it to a type (at the moment we would have to use template haskell - but there is no reason the compiler cannot be a little more aggresive in applying functions to constants at compile time, in which case we can use polymorphic recursion)... So the format string can be lifted to something like: format :: HCons (ConstString (HCons ParString (HCons ParInt HNil))) we can then construct a class to recurse on this type checking each of the 'Par...' types against the argument to ensure the type is correct. The code would look like Oleg's variable argument function code with an additional class parameter for the format-type. Keean. Henning Thielemann wrote:
On 16 Nov 2004, Peter Simons wrote:
I'm not sure, though, whether this is the case here, because vsnprintf in Haskell still is type-safe. You'll get more complicated error messages, the memory footprint might be worse, but it still _is_ robust code.
Yes and no. It can't be checked statically if the number of placeholders matches the number of arguments. It can't be checked statically if the types of placeholders match the types of arguments. It is not possible to create functions with more than one variable length parameter lists.
Dear ${customer},
are you interested in making ${phantasy-number} fast?
and pass a function (String -> a) to vsnprintf which does the lookup. I'm not sure how having different types of values in the dictionary plays into this, though.
The function MissingH.Printf.sprintf is probably the better choice, but one could even replace [Value] by [String]. The conversion from any type to String can be easily done using 'show' by the caller. Though it gives the author of the format string less control over the formatting of particular types like numbers.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Tue, Nov 16, 2004 at 12:21:41PM +0100, Henning Thielemann wrote:
The function MissingH.Printf.sprintf is probably the better choice, but one could even replace [Value] by [String]. The conversion from any type to String can be easily done using 'show' by the caller. Though it gives the author of the format string less control over the formatting of particular types like numbers.
It seems to me that control over formatting of numbers is the only reason to use anything printf-like... but perhaps that's just because of the frequency with which I print (and read) doubles. -- David Roundy http://www.darcs.net

On 2004-11-16, Henning Thielemann
On 16 Nov 2004, Peter Simons wrote:
Yes and no. It can't be checked statically if the number of placeholders matches the number of arguments. It can't be checked statically if the types of placeholders match the types of arguments. It is not possible to create functions with more than one variable length parameter lists.
That's a particular problem, since this was one of the motivations for getting a printf out there. It makes a very nice way to produce nice column-aligned data from a variety of numeric and string sources, and provides a good deal of control over numeric output formats, too. If I encounter %07.3f, I can't just do a read on a string passed to me, hoping that I'd get a valid Double (or Rational or whatever). One of the annoying things is that show on a Rational produces a value that is incompatible with show on a Double, and the read functions for these two types can't understand each other's string formats. (Urg.)
and pass a function (String -> a) to vsnprintf which does the lookup. I'm not sure how having different types of values in the dictionary plays into this, though.
There is a function like that in the MissingH.Printf source, but it is not exported at present (rather it is used to support the association list and FiniteMap versions of this feature). It could easily be exported, though. -- John

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Peter Simons wrote: | > You can save even more characters: | | > msg = verb "i = " . shows 12 . verb "\tj = " $ "test" | | Right! One more reason to use ShowS-style. :-) There is a probleme with ShowS though: it is not internationalizable at all. Strings like printf's or with any kind of variable substitution is required for proper internationalization / localization. Jérémy. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.5 (GNU/Linux) iD8DBQFBmeed2PUjs9fQ72URAt6hAJ925nXOwtYwVKUtQyBpVA5syjVzzgCgoZLZ I8G5ZEAz9BPiqC3YPN0ybGU= =CUth -----END PGP SIGNATURE-----

There is a probleme with ShowS though: it is not internationalizable at all. Strings like printf's or with any kind of variable substitution is required for proper internationalization / localization. Printf is not adequate for internationalization either, because word (and thus
On 2004 November 16 Tuesday 06:42, Jérémy Bobbio wrote: parameter) ordering may vary among languages. Note that MissingH.Printf addresses this with a feature which supports keys in format items, e.g. %(item1)s.

There is a probleme with ShowS though: it is not internationalizable at all. Strings like printf's or with any kind of variable substitution is required for proper internationalization / localization. Printf is not adequate for internationalization either, because word (and thus
On 2004 November 16 Tuesday 06:42, Jérémy Bobbio wrote: parameter) ordering may vary among languages.
POSIX / SUS 2001 printf() supports positional arguments, like this:
printf("%2$d %1$s\n","days of christmas",12);
This is required for l10n, as you say..
--KW 8-)
--
Keith Wansbrough

Scott Turner wrote:
On 2004 November 16 Tuesday 06:42, Jérémy Bobbio wrote:
There is a probleme with ShowS though: it is not internationalizable at all. Strings like printf's or with any kind of variable substitution is required for proper internationalization / localization.
Printf is not adequate for internationalization either, because word (and thus parameter) ordering may vary among languages. Note that MissingH.Printf addresses this with a feature which supports keys in format items, e.g. %(item1)s.
This is what it boils down to for me. A post by Malcolm Wallace to one of the Haskell lists a few years back convinced me that: Internationalization is a killer application for printf-style format-string-based functions. I have seen approximately 32767 different proposals for printf with strong static typechecking, using anything from specialized data types to depedently-typed functions to compile-time reflection. None of these appears to be able to solve the simple problem of allowing localization (or general fiddling with the formatting) without recompiling and relinking. In that respect there will always be a place for dynamically-checked printf, and John's library really seems to be doing The Right Thing. I remain much more skeptical that there's a need for a statically-checked printf which uses format strings. ShowS and related approaches work pretty well in my experience (though it'd be nice to see standardized support for flexible number formatting). A Challenge To The Type System Hackers: Is there a way to get the best of both worlds? To do static checking on format strings when they're available, but fall back to dynamic checking when they are not? Perhaps we provide a canonical format string, or use some other sort of hackery, and then check the actual string dynamically. -Jan-Willem Maessen

On 2004-11-16, Peter Simons
I know memory is expensive, that's why only the last two digits of year numbers are stored. :-]
I understand what you're getting at -- and I find it annoying, too, when people sacrifice robustness for comfort.
In this particular case, the printf functions without the leading "v" operate on a list in the manner some have suggested. I find the "v" forms preferable since they are easier to type, though I understand the case against them.
the conclusion that for this particular use-case hard-coded place-holders (like %s, %d, etc.) are not that useful. You'd like to be able to (easily!) extend the function, to offer a more general variable substitution, like sh(1) does. So that you could write
Dear ${customer},
Indeed, I've already done that in the Python style. From [1]: ----------------- As a special extension to the printf() format string syntax, special functions can take a key name in the format string. This key will then be looked up in an association list or FiniteMap passed in. Python programmers will find this very similar to Python's % operator, which can look up inside dicts. Here's an example: import MissingH.Printf al = [("item1", v "Test One"), ("blah", v (5::Int)), ("zip", v (3.14::Float))] main :: IO () main = do printfAL "%(item1)s: %(blah)03d, %(zip)06.3f; %(item1)s\n" al This will print: Test One: 005, 03.140; Test One ------------------- There are also printfFM functions that work with FiniteMaps in a similar manner. [1] http://gopher.quux.org:70/devel/missingh/html/MissingH.Printf.html#6

On Tuesday 16 November 2004 10:37, Henning Thielemann wrote:
Variable length argument lists are really a mess. Why are people so keen on them? What is the advantage over a plain list as single argument? Is vsprintf "%s, your age is %s\n" ["John", show (10::Integer)] really too complicated?
The implementation of printf in ocaml, for example, is not only type-safe, but "more type safe" than passing a list, because the number and type of arguments is known at compile time. V.

On Wed, 17 Nov 2004, Vincenzo Ciancia wrote:
On Tuesday 16 November 2004 10:37, Henning Thielemann wrote:
Variable length argument lists are really a mess. Why are people so keen on them? What is the advantage over a plain list as single argument? Is vsprintf "%s, your age is %s\n" ["John", show (10::Integer)] really too complicated?
The implementation of printf in ocaml, for example, is not only type-safe, but "more type safe" than passing a list, because the number and type of arguments is known at compile time.
Can this be also achieved for extended versions implemented by OCaml users, say for printf's with more placeholder types? What about variable format strings? I assume that it is very hard to solve all these problems and I wonder if it is worth the trouble ...

On 2004-11-17, Vincenzo Ciancia
On Tuesday 16 November 2004 10:37, Henning Thielemann wrote:
Variable length argument lists are really a mess. Why are people so keen on them? What is the advantage over a plain list as single argument? Is vsprintf "%s, your age is %s\n" ["John", show (10::Integer)] really too complicated?
The implementation of printf in ocaml, for example, is not only type-safe, but "more type safe" than passing a list, because the number and type of arguments is known at compile time.
On the other hand, the OCaml printf is, unless I'm very mistaken, handled specially by the compiler. That is, the compiler looks out for format strings and converts them to the weirdo format4 things that OCaml uses. That means that one could not implement one's own printf using pure OCaml. Though a person could use camlp4, but then that's back to the same place as TH. OCaml's printf also is limited in that it can take only a string literal for the format string, and not a string in a variable. -- John
participants (14)
-
Andreas Rossberg
-
Ben Rudiak-Gould
-
David Roundy
-
Henning Thielemann
-
Jan-Willem Maessen
-
John Goerzen
-
Jon Fairbairn
-
Jérémy Bobbio
-
Keean Schupke
-
Keith Wansbrough
-
Matthew Walton
-
Peter Simons
-
Scott Turner
-
Vincenzo Ciancia