Disclipline for re-use: Slim instances
kahl at cas.mcmaster.ca
kahl at cas.mcmaster.ca
Tue Sep 5 13:20:45 EDT 2006
Although the trigger for this comes from a thread in glasgow-haskell-users,
I think that for the general problem,
the libraries list is the right forum.
Of course, the prelude and standard libraries aspect of
haskell-prime should be influenced as well,
but I am hesitant to cross-post.
Serge D. Mechveliani <mechvel at botik.ru> wrote:
>
> I need to print in a special way the data of
> [Equation], (Term, Term), [(Term, Term)], (Equation, Equation).
>
> The first can be by defining showList in instance Show Equation.
> But Show has not a method of showPair. So, I need to write the
> function showsTermPair and to use it together with another home-made
> function showsListGeneric.
I have been bitten by this, too, so let me make the point more explicit:
For lists, the Haskell98 report contains:
| class Show a where
| showsPrec :: Int -> a -> ShowS
| show :: a -> String
| showList :: [a] -> ShowS
|
| -- Mimimal complete definition:
| -- show or showsPrec
| showsPrec _ x s = show x ++ s
|
| show x = showsPrec 0 x ""
|
| showList [] = showString "[]"
| showList (x:xs) = showChar '[' . shows x . showl xs
| where showl [] = showChar ']'
| showl (x:xs) = showChar ',' . shows x .
| showl xs
The proposed function showsListGeneric would of course be:
> showsListGeneric shows [] = showString "[]"
> showsListGeneric shows (x:xs) = showChar '[' . shows x . showl xs
> where showl [] = showChar ']'
> showl (x:xs) = showChar ',' . shows x .
> showl xs
If this was defined in the prelude, then the default definition in the
class declaration would become:
> class Show a where
> -- [...]
>
> showList = showsListGeneric shows
Similarly, for pairs the prelude has:
| instance (Show a, Show b) => Show (a,b) where
| showsPrec p (x,y) = showChar '(' . shows x . showChar ',' .
| shows y . showChar ')'
The suggested function showPair is of course:
> showPair shows1 shows2 (x,y) = showChar '(' . shows1 x . showChar ',' .
> shows2 y . showChar ')'
Again, if this was defined in the prelude,
the instance declaration would become:
> instance (Show a, Show b) => Show (a,b) where
> showsPrec p (x,y) = showPair shows shows
The problem we encounter here is that instance declarations and default
definitions contain unnamed functions, which however frequently are also
useful in other contexts.
Since it does not appear to be attractive or easy to introduce (into the
language definition) a naming scheme for these functions,
I propose to introduce the following discipline for all library developers:
*********************************************
* All instance declarations should be slim. *
*********************************************
I.e.:
* Instance declarations (and default definitions)
should never contain non-trivial function definitions.
* Instance declarations (and default definitions)
should only provide ``plumbing'' to make existing functions
accessible via the type class resolution mechanism.
* The ``plumbed'' functions should always be exported
(since instances are always exported).
Since the naming will not always be straight-forward,
the last point is particularly important
and would enable more re-use
and less re-invention of mostly trivial wheels.
I append the ``TextFunctors'' module
which I mostly pulled out of GHC's internals;
the commented-out part of the export list shows where these belong
in the code base of GHC;
in my opinion, they should of course also be in the standard libraries.
Wolfram
======= TextFunctors.lhs ================================================
\section{Read and Show Functors}
\begin{code}
module TextFunctors
( module TextFunctors
-- -- the exported functions should really be imported
-- -- from the following locations:
-- , GHC.Show.showsPrecMaybe
-- , GHC.Show.showsPrecEither
-- , GHC.Show.showsPrecTup2
-- , GHC.Show.showsPrecTup3
-- , GHC.Show.showsPrecTup4
-- , GHC.Show.showsPrecTup5
-- , GHC.Read.readPrecMaybe
-- , GHC.Read.readPrecEither
-- , GHC.Read.readPrecArray
-- , GHC.Read.readPrecTup2
-- , GHC.Read.readPrecTup3
-- , GHC.Read.readPrecTup4
-- , GHC.Read.readPrecTup5
, GHC.Read.Read(..)
) where
import qualified GHC.Show
import qualified GHC.Read
-- the following imports are necessary for the read functions:
import Text.ParserCombinators.ReadPrec
import Array
import qualified Text.Read.Lex as L
\end{code}
%{{{ \subsection{Show}
\subsection{Show}
\begin{code}
type ShowSPrec a = Int -> a -> ShowS
showsPrecList :: ShowSPrec a -> ShowSPrec [a]
showsPrecList showsPrec p = GHC.Show.showList__ (showsPrec 0)
\end{code}
\begin{code}
showsPrecMaybe :: ShowSPrec a -> ShowSPrec (Maybe a)
showsPrecMaybe _showsPrec _p Nothing s = showString "Nothing" s
showsPrecMaybe showsPrec p (Just x) s
= (showParen (p > appPrec) $
showString "Just " .
showsPrec appPrec1 x) s
\end{code}
\begin{code}
showsPrecEither :: ShowSPrec a -> ShowSPrec b -> ShowSPrec (Either a b)
showsPrecEither showsPrecA showsPrecB p e s =
(showParen (p > appPrec) $
case e of
Left a -> showString "Left " . showsPrecA appPrec1 a
Right b -> showString "Right " . showsPrecB appPrec1 b
) s
\end{code}
\begin{code}
showsPrecTup2 showsPrecA showsPrecB _ (x,y) s =
(showChar '(' . showsPrecA noPrec x . showChar ',' . showChar ' ' .
showsPrecB noPrec y . showChar ')'
) s
\end{code}
\begin{code}
showsPrecTup3 showsPrecA showsPrecB showsPrecC _ (x,y,z) s =
(showChar '(' . showsPrecA noPrec x . showChar ',' . showChar ' ' .
showsPrecB noPrec y . showChar ',' . showChar ' ' .
showsPrecC noPrec z . showChar ')'
) s
\end{code}
\begin{code}
showsPrecTup4 showsPrecA showsPrecB showsPrecC showsPrecD _ (x,y,z,u) s =
(showChar '(' . showsPrecA noPrec x . showChar ',' . showChar ' ' .
showsPrecB noPrec y . showChar ',' . showChar ' ' .
showsPrecC noPrec z . showChar ',' . showChar ' ' .
showsPrecD noPrec u . showChar ')'
) s
\end{code}
\begin{code}
showsPrecTup5 showsPrecA showsPrecB showsPrecC showsPrecD showsPrecE _ (x,y,z,u,v) s =
(showChar '(' . showsPrecA noPrec x . showChar ',' . showChar ' ' .
showsPrecB noPrec y . showChar ',' . showChar ' ' .
showsPrecC noPrec z . showChar ',' . showChar ' ' .
showsPrecD noPrec u . showChar ',' . showChar ' ' .
showsPrecE noPrec v . showChar ')'
) s
\end{code}
\begin{code}
noPrec :: Int
noPrec = 0
appPrec = GHC.Show.appPrec
appPrec1 = GHC.Show.appPrec1
\end{code}
%}}}
%{{{ \subsection{Read}
\subsection{Read}
\begin{code}
readPrecList = GHC.Read.list
\end{code}
\begin{code}
readPrecMaybe :: ReadPrec a -> ReadPrec (Maybe a)
readPrecMaybe readPrec =
parens
(do L.Ident "Nothing" <- lexP
return Nothing
+++
prec appPrec (
do L.Ident "Just" <- lexP
x <- step readPrec
return (Just x))
)
\end{code}
\begin{code}
readPrecEither :: ReadPrec a -> ReadPrec b -> ReadPrec (Either a b)
readPrecEither readPrecA readPrecB =
parens
( prec appPrec
( do L.Ident "Left" <- lexP
x <- step readPrecA
return (Left x)
+++
do L.Ident "Right" <- lexP
y <- step readPrecB
return (Right y)
)
)
\end{code}
\begin{code}
readPrecArray :: Ix i => ReadPrec i -> ReadPrec a -> ReadPrec (Array i a)
readPrecArray readPrecI readPrecA = parens $ prec appPrec $
do L.Ident "array" <- lexP
bounds <- step (readPrecTup2 readPrecI readPrecI)
vals <- step (GHC.Read.list (readPrecTup2 readPrecI readPrecA))
return (array bounds vals)
\end{code}
\begin{code}
readPrecTup2 readPrecA readPrecB =
parens
( paren
( do x <- readPrecA
L.Punc "," <- lexP
y <- readPrecB
return (x,y)
)
)
\end{code}
\begin{code}
readPrecTup3 readPrecA readPrecB readPrecC =
parens
( paren
( do x <- readPrecA
L.Punc "," <- lexP
y <- readPrecB
L.Punc "," <- lexP
z <- readPrecC
return (x,y,z)
)
)
\end{code}
\begin{code}
readPrecTup4 readPrecA readPrecB readPrecC readPrecD =
parens
( paren
( do w <- readPrecA
L.Punc "," <- lexP
x <- readPrecB
L.Punc "," <- lexP
y <- readPrecC
L.Punc "," <- lexP
z <- readPrecD
return (w,x,y,z)
)
)
\end{code}
\begin{code}
readPrecTup5 readPrecA readPrecB readPrecC readPrecD readPrecE =
parens
( paren
( do v <- readPrecA
L.Punc "," <- lexP
w <- readPrecB
L.Punc "," <- lexP
x <- readPrecC
L.Punc "," <- lexP
y <- readPrecD
L.Punc "," <- lexP
z <- readPrecE
return (v,w,x,y,z)
)
)
\end{code}
\begin{code}
paren = GHC.Read.paren
parens = GHC.Read.parens
lexP = GHC.Read.lexP
\end{code}
%}}}
%{{{ EMACS lv
% Local Variables:
% folded-file: t
% fold-internal-margins: 0
% eval: (fold-set-marks "%{{{ " "%}}}")
% eval: (fold-whole-buffer)
% end:
%}}}
More information about the Libraries
mailing list