Skip to content

Instantly share code, notes, and snippets.

@adituv
Created January 26, 2018 01:29
Show Gist options
  • Save adituv/5486a7f6aebbf3945b604d6304cc1366 to your computer and use it in GitHub Desktop.
Save adituv/5486a7f6aebbf3945b604d6304cc1366 to your computer and use it in GitHub Desktop.
Show ratios as a mixed fraction
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
module Text.Show.Ratio(showMixed, showsMixed, showsPrecMixed) where
import Data.Ratio
-- | Convert a @'Ratio' a@ to a readable string, where the ratio is
-- expressed as a mixed fraction in its simplest form. `showsPrecMixed`
-- is designed for use as part of another show implementation, and so
-- adds parentheses when the operator precedence of the enclosing context
-- is greater than that of '+' (a value of 6)
showsPrecMixed :: (Show a, Integral a)
=> Int -- ^ the operator precedence of the enclosing context
-- (a number from @0@ to @11@). Function application has
-- precedence @10@
-> Ratio a -- ^ The ratio to be converted to a 'String'
-> ShowS
showsPrecMixed p x = showParen (p > 6) $ showsMixed x
-- | Convert a @'Ratio' a@ to a readable string, where the ratio is
-- expressed as a mixed fraction in its simplest form
showsMixed :: (Show a, Integral a) => Ratio a -> ShowS
showsMixed (n :% d) = quotStr . showString op . remStr
where
(q,r) = n `quotRem` d
op = if q /= 0 && r > 0 -- Three cases where q /= 0:
then "+" -- * r > 0: show r does not include its sign
else "" -- * r < 0: show r includes its sign.
-- * r == 0: r will be excluded from the output
quotStr = if | q == 0 && r == 0 -> showString "0"
| q == 0 -> id
| otherwise -> shows q
remStr = if r == 0
then id
else shows r . showString "/" . shows d
-- NB @showString "" === (""++) === id@. It would probably be optimised out
-- correctly but I used @id@ instead of @showString ""@ just to be sure
-- | Convert a @'Ratio' a@ to a readable string, where the ratio is
-- expressed as a mixed fraction in its simplest form
showMixed :: (Show a, Integral a) => Ratio a -> String
showMixed = flip showsMixed ""
-- Utility pattern as the constructor of Ratio is not exposed
pattern n :% d <- (splitRatio -> (n,d))
splitRatio :: Ratio a -> (a,a)
splitRatio x = (numerator x, denominator x)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment