3

Simple inclusion of a Rational in printf output can be accomplished using %s and show:

> printf "... %s ...\n" (show $ 123 % 456)
... 41 % 152 ...

However, it would be useful to be able to use the %f format code as well:

> printf "... %.30f ...\n" (123 % 456)
... 0.269736842105263157894736842105 ...

Note that formatting via a conversion to Double (eg using fromRational) would not allow the arbitrary precision I'm looking for:

> printf "... %.30f ...\n" (fromRational $ 123 % 456)
... 0.269736842105263160000000000000 ...

The %v code could be used to produce the same output as show, and perhaps some modifiers could be allowed with it.

Is there any reason why this shouldn't be accomplished with a custom formatter (an instance of PrintfArg (Ratio a))?

For reference, here's a standalone function that I'm using as a partial workaround:

showDecimal :: RealFrac a => Int -> a -> String
showDecimal n r = printf "%d.%0*d" (i :: Integer) n (abs d :: Integer)
  where (i, d) = (round $ r * 10^n) `quotRem` (10^n)

> printf "... %35s ...\n" (showDecimal 30 $ 123 % 456)
...    0.269736842105263157894736842105 ...

However, this doesn't allow the use of modifiers, and involves specifying widths in separate places.

Neil Mayhew
  • 14,206
  • 3
  • 33
  • 25
  • 2
    I guess the answer could be "because nobody wrote that", as boring as that might sound. – chi Aug 08 '18 at 22:35
  • 1
    Yeah. You may want to implement the instance and file a ticket for including it in `base`! — Anyway note that `printf` is not really Haskell-idiomatic – it essentially mimics C's `printf` by using a strange typeclass hack to allow for variadic functions. I recommend you check out the [fmt](http://hackage.haskell.org/package/fmt) library, which takes a much simpler approach and particularly is designed to make use of the `Show` typeclass when that makes sense. With `fmt`, you can simply write `"... "+||123%456||+" ...\n"`. – leftaroundabout Aug 08 '18 at 22:46
  • 2
    Because `printf` was originally written for Lazy ML and LML didn’t have rational at the time. When I transliterated it to Haskell I simply didn’t think of it. (The original printf code dates back to about 1983.) – augustss Aug 09 '18 at 02:27
  • Also check out https://stackoverflow.com/questions/30931369/how-to-convert-a-rational-into-a-pretty-string for formatting code. I like my version since it allows you to find the repetition in the expansion. – augustss Aug 09 '18 at 03:19
  • @leftaroundabout I didn't know about `fmt — thanks for the tip. I plan to use that more in future. However, as I found with `printf` vs `iostream` in C++, good old `printf` is still great when you want to produce tabular output, because you can control widths and precisions so succinctly. – Neil Mayhew Aug 10 '18 at 16:34
  • @augustss Thanks for the background info — fascinating! I like your formatting implementation, too, that handles the recurring digits. However, I think that should be selected with a modifier because the most common use case would be for displaying an exact number of digits, in order to make tabulated output align. – Neil Mayhew Aug 10 '18 at 16:38

1 Answers1

1

The consensus in the comments seems to be, because nobody got around to writing one, and it seems like a good idea.

I'm answering my own question so it can be marked as answered. I'll add a comment if I or anyone else get around to implementing it.

Neil Mayhew
  • 14,206
  • 3
  • 33
  • 25