<div dir="ltr">It happens very often that I want to convert a number of values to strings and<br>concatenate those strings into one. No surprise there, of course. Well, I'd<br>prefer to do it efficiently and with as little code as necessary.<br>
<br><span style="font-family: courier new,monospace;">> {-# LANGUAGE TypeSynonymInstances #-}</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">> module ShowsDemo where</span><br style="font-family: courier new,monospace;">
<br>Let's say I want to generate the string "(42 abc)" starting with a number and a<br>string stored in variables.<br><br><span style="font-family: courier new,monospace;">> n = 42 :: Int</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">> s = "abc"</span><br style="font-family: courier new,monospace;"><br>What are my options?<br><br>There's the obvious approach that's described in every tutorial, book, and<br>
research paper (for didactic purposes, of course).<br><br><span style="font-family: courier new,monospace;">> ex1 = "(" ++ show n ++ " " ++ s ++ ")"</span><br style="font-family: courier new,monospace;">
<br>It's pretty concise, but it's horribly inefficient due to the use of (++).<br><br>Then, there's the ShowS approach.<br><br><span style="font-family: courier new,monospace;">> ex2 = showChar '(' . shows n . showChar ' ' . showString s . showChar ')' $ ""</span><br style="font-family: courier new,monospace;">
<br>This is more efficient, but now the code has bloated up a lot.<br><br>Why can't I have my cake and eat it, too? I want to write with as little code as<br>|ex1| (or less if possible), and I want it to be as efficient as |ex2|.<br>
<br>I propose this example as an improvement.<br><br><span style="font-family: courier new,monospace;">> ex3 = '(' .+. n .+. ' ' .+. s .$. ')'</span><br style="font-family: courier new,monospace;">
<br>It uses a class I'm calling |Shows|. The class has one method that simply<br>converts a value to the type |ShowS|, where |ShowS| is a type synonym for<br>|String -> String| and is defined in the Prelude.<br><br>
<span style="font-family: courier new,monospace;">> class Shows a where</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">> toShows :: a -> ShowS</span><br style="font-family: courier new,monospace;">
<br>Notice the lack of context involving the |Show| class. That's important, because<br>it allows us to create more instances than we could if we were restricted by<br>|(Show a) => ...|, esp. the |ShowS| instance below.<br>
<br>The instances for types are all very simple. Most will appear like the instance<br>for |Int|.<br><br><span style="font-family: courier new,monospace;">> instance Shows Int where</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">> toShows = shows</span><br style="font-family: courier new,monospace;"><br>Since we don't have |Show| in the class context above, we can't make this a<br>default method.<br>
<br>We need a few special instances for |Char| and |String| to make these types<br>convenient to use in the expected way.<br><br><span style="font-family: courier new,monospace;">> instance Shows Char where</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">> toShows = showChar</span><br style="font-family: courier new,monospace;"><br><span style="font-family: courier new,monospace;">> instance Shows String where</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">> toShows = showString</span><br style="font-family: courier new,monospace;"><br>We also need an instance for |ShowS| in order to facilitate concatenation.<br><br><span style="font-family: courier new,monospace;">> instance Shows ShowS where</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">> toShows = id</span><br style="font-family: courier new,monospace;"><br>Now, we define a few operators that use |toShows| and make our lives easier and<br>our code more concise.<br>
<br>The |(.+.)| replaces list appending, |(++)|, in |ex1| and function composition,<br>|.|, in |ex2|.<br><br><span style="font-family: courier new,monospace;">> infixl 5 .+.</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">> (.+.) :: (Shows a, Shows b) => a -> b -> ShowS</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">> a .+. b = toShows a . toShows b</span><br style="font-family: courier new,monospace;">
<br>The |(.$.)| replaces the need for |($)| in |ex2|.<br><br><span style="font-family: courier new,monospace;">> infixl 4 .$.</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">> (.$.) :: (Shows a, Shows b) => a -> b -> String</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">> a .$. b = (a .+. b) ""</span><br style="font-family: courier new,monospace;"><br>I would find something like this very useful. I'm guessing the idea can be<br>
applied to |ByteString| as well. Does it exist in some other form?<br><br>Sean<br></div>