How to define Show [MyType] ?
I am trying to define instance Show[MyType] so show (x:xs :: MyType) would return a single string where substrings corresponding to list elements will be separated by "\n". This would allow pretty printing of MyType list in several lines instead of one, as default Show does for lists. For example: data ShipInfo = Ship { name :: String, kind :: String, canons :: Int } deriving Show s1 = Ship {name ="HMS Fly", kind = "sloop", canons=16} s2 = Ship {name ="HMS Surprise", kind = "frigate", canons=42} -- Yet when I try to define: instance (Show ShipInfo) => Show [ShipInfo] where show (x:xs) = "<" ++ show x ++ ">" ++ show xs -- I get this error: Illegal instance declaration for `Show [ShipInfo]' (The instance type must be of form (T a b c) where T is not a synonym, and a,b,c are distinct type variables) In the instance declaration for `Show [ShipInfo]' Failed, modules loaded: none. -- On the other hand this definition: instance (Show a) => Show [a] where show (x:xs) = "<" ++ show x ++ ">" ++ show xs -- Gives a different error: Duplicate instance declarations: instance (Show a) => Show [a] -- Defined at C:/wks/haskell-wks/ShowMatrix.hs:37:0 instance (Show a) => Show [a] -- Defined in GHC.Show Failed, modules loaded: none. -- Note the last error implicitly tells us that defining Show [a] is possible in principle! -- In fact it already defined in GHC.Show !!! -- How to define Show [MyType] ? Thanks! Dmitri
On Fri, Dec 5, 2008 at 1:29 AM, Martijn van Steenbergen < martijn@van.steenbergen.nl> wrote:
Dmitri O.Kondratiev wrote:
-- How to define Show [MyType] ?
Define instance Show MyType and implement not only show (for 1 value of MyType) but also showList, which Show provides as well. You can do all the magic in there.
HTH,
Martijn.
Thanks everybody for your help! I tried to implement showList, but get the same error: {-- -- from Prelude: type *ShowS* = String<file:///C:/usr/ghc-6.6.1/doc/html/libraries/base/Prelude.html#t%3AString>-> String<file:///C:/usr/ghc-6.6.1/doc/html/libraries/base/Prelude.html#t%3AString> *showList* :: [a] -> ShowS<file:///C:/usr/ghc-6.6.1/doc/html/libraries/base/Prelude.html#t%3AShowS> --} myShows::ShowS myShows s = s ++ "\n" data ShipInfo = Ship { name :: String, kind :: String, canons :: Int } deriving Show -- I get this error again: instance (Show [ShipInfo]) where showList [] = myShows [] showList (x:xs) = myShows "x" ++ showList xs Illegal instance declaration for `Show [ShipInfo]' (The instance type must be of form (T a b c) where T is not a synonym, and a,b,c are distinct type variables) In the instance declaration for `Show [ShipInfo]' Failed, modules loaded: none. -- What am I doing wrong? Thanks!
Am Samstag, 6. Dezember 2008 00:13 schrieb Dmitri O.Kondratiev:
Thanks everybody for your help! I tried to implement showList, but get the same error:
{-- -- from Prelude: type *ShowS* = String<file:///C:/usr/ghc-6.6.1/doc/html/libraries/base/Prelude.html#t%3ASt ring>-> String<file:///C:/usr/ghc-6.6.1/doc/html/libraries/base/Prelude.html#t%3ASt ring> *showList* :: [a] -> ShowS<file:///C:/usr/ghc-6.6.1/doc/html/libraries/base/Prelude.html#t%3ASho wS> --}
myShows::ShowS myShows s = s ++ "\n"
data ShipInfo = Ship { name :: String, kind :: String, canons :: Int } deriving Show
No, if you derive the Show instance for ShipInfo, it automatically implements the standard showList, too. You have to do it yourself: data ShipInfo = Ship { name :: String, kind :: String, canons :: Int } instance Show ShipInfo where showsPrec p (Ship name kind canons) = ... showList xs = showString (unlines $ map show xs) or whatever you want for showList. However, somebody said it before, the Show instance should not be used for pretty-printing.
-- I get this error again: instance (Show [ShipInfo]) where showList [] = myShows [] showList (x:xs) = myShows "x" ++ showList xs
Illegal instance declaration for `Show [ShipInfo]' (The instance type must be of form (T a b c) where T is not a synonym, and a,b,c are distinct type variables) In the instance declaration for `Show [ShipInfo]' Failed, modules loaded: none.
-- What am I doing wrong? Thanks!
Daniel, thanks! I used your advice, it works, yet some more questions, if you don't mind :) So: {-- Why showList returns a function of type: type ShowS = String -> String In fact showList returns a function which already contains all values from list converted to a single string. Why return function instead of a ready to use string? --} data ShipInfo = Ship { name :: String, kind :: String, cannons :: Int } s1 = Ship {name ="HMS Fly", kind = "sloop", cannons=16} s2 = Ship {name ="HMS Surprise", kind = "frigate", cannons=42} fleet = [s1,s2] instance Show ShipInfo where show x = "Ship "++ show (name x) ++" is a " ++ show(kind x) ++ " with " ++ show (cannons x) ++ " cannons " showList xs = showString (foldr (\x y -> x ++ " <*> "++y) [] (map show xs)) {-- Now when I type 'fleet' I get: *ShowFleet> fleet Ship "HMS Fly" is a "sloop" with 16 cannons <*> Ship "HMS Surprise" is a "frigate" with 42 cannons <*> *ShowFleet> On the other hand evaluating t2 : --} t2 xs = foldr (\x y -> x ++ " <*> "++y) [] (map show xs) {-- provides: *ShowFleet> t2 fleet "Ship \"HMS Fly\" is a \"sloop\" with 16 cannons <*> Ship \"HMS Surprise\" is a \"frigate\" with 42 cannons <*> " *ShowFleet> More questions: 1)Where characters \" come from in this case? 2)How showList function 'removes' characters \" from the output? What magic goes behind the scenes here? --} Thanks! Dmitri On 12/6/08, Daniel Fischer <daniel.is.fischer@web.de> wrote:
Am Samstag, 6. Dezember 2008 00:13 schrieb Dmitri O.Kondratiev:
Thanks everybody for your help! I tried to implement showList, but get the same error:
{-- -- from Prelude: type *ShowS* = String<file:///C:/usr/ghc-6.6.1/doc/html/libraries/base/Prelude.html#t%3ASt ring>-> String<file:///C:/usr/ghc-6.6.1/doc/html/libraries/base/Prelude.html#t%3ASt ring> *showList* :: [a] -> ShowS<file:///C:/usr/ghc-6.6.1/doc/html/libraries/base/Prelude.html#t%3ASho wS> --}
myShows::ShowS myShows s = s ++ "\n"
data ShipInfo = Ship { name :: String, kind :: String, canons :: Int } deriving Show
No, if you derive the Show instance for ShipInfo, it automatically implements the standard showList, too. You have to do it yourself:
data ShipInfo = Ship { name :: String, kind :: String, canons :: Int }
instance Show ShipInfo where showsPrec p (Ship name kind canons) = ... showList xs = showString (unlines $ map show xs)
or whatever you want for showList. However, somebody said it before, the Show instance should not be used for pretty-printing.
-- I get this error again: instance (Show [ShipInfo]) where showList [] = myShows [] showList (x:xs) = myShows "x" ++ showList xs
Illegal instance declaration for `Show [ShipInfo]' (The instance type must be of form (T a b c) where T is not a synonym, and a,b,c are distinct type variables) In the instance declaration for `Show [ShipInfo]' Failed, modules loaded: none.
-- What am I doing wrong? Thanks!
-- Haste makes waste - Lose not a moment! Dmitri O. Kondratiev dokondr@gmail.com http://www.geocities.com/dkondr
On Sat, Dec 6, 2008 at 3:39 PM, Dmitri O.Kondratiev <dokondr@gmail.com> wrote:
Daniel, thanks! I used your advice, it works, yet some more questions, if you don't mind :) So: {-- Why showList returns a function of type: type ShowS = String -> String In fact showList returns a function which already contains all values from list converted to a single string. Why return function instead of a ready to use string? --}
ShowS is for efficiency; consider evaluating this: ( ( ( "1" ++ "2" ) ++ "3" ) ++ "4" ) ++ "5" ("1" ++ "2") evaluates to "12", allocating 1 list cell ("12" ++ "3") evalutes to "123", allocating 2 list cells ("123" ++ "4") evaluates to "1234", allocating 3 list cells ("1234" ++ "5") evaluates to "12345", allocating 4 list cells In general, n left-recursive applications of ++ take O(n^2) time and space. Now, instead, if you represent each string as a function which concatenates itself with its argument, you end up with this: ( '1': ), ( '2': ), etc. You can then "concatenate" these functions via function composition: ( ( ( ( '1': ) . ( '2':) ) . ( '3': ) ) . ( '4': ) ) . ( '5': ) This is already in normal form; but watch what happens when we apply it to []: ( ( ( ( ( '1': ) . ( '2':) ) . ( '3': ) ) . ( '4': ) ) . ( '5': ) ) "" => ( ( ( ( '1': ) . ( '2':) ) . ( '3': ) ) . ( '4': ) ) "5" , allocating 1 cons cell => ( ( ( '1': ) . ( '2':) ) . ( '3': ) ) "45", allocating 1 cons cell => ( ( '1': ) . ( '2':) ) "345", allocating 1 cons cell => ( '1': ) "2345", allocating 1 cons cell => "12345", allocating 1 cons cell This has allocations and time *linear* in the number of function compositions.
*ShowFleet> t2 fleet "Ship \"HMS Fly\" is a \"sloop\" with 16 cannons <*> Ship \"HMS Surprise\" is a \"frigate\" with 42 cannons <*> " *ShowFleet>
More questions: 1)Where characters \" come from in this case? 2)How showList function 'removes' characters \" from the output? What magic goes behind the scenes here?
1) They come from the "show" instance for String. Try "putStrLn (t2 fleet)" instead. 2) There's no magic; if the result at the ghci prompt is an instance of Show, it calls show and prints the result. In the first case, the result was a [Fleet], which is an instance of Show, so show is called, which (in the instance (Show a => Show [a])) calls showList from Show Fleet, and prints the result. In the second case, you are evaluating a String; then showList from Show Char gives you the representation. It's the same as this: ghci> [1,2,3] [1,2,3] ghci> show [1,2,3] "[1,2,3]" ghci> "hello\nworld\n" "hello\nworld\n" ghci> putStr "hello\nworld\n" hello world -- ryan
1) They come from the "show" instance for String. Try "putStrLn (t2 fleet)" instead.
<pedant> The show instance for Char. </pedant> (I know you know this, I just have this weird fascination with the showList wart, although for the life of me I can't think of a better way of doing it) cheers, Fraser.
On Sun, Dec 7, 2008 at 1:51 AM, Fraser Wilson <blancolioni@gmail.com> wrote:
(I know you know this, I just have this weird fascination with the showList wart, although for the life of me I can't think of a better way of doing it)
Well, if you extend the compiler's core language with "typecase", you can solve a lot of these problems. Then the implementation of typeclasses changes from "dictionary-passing" to "type-passing", and overlapping instances can be resolved by dynamic dispatch. For example, the following program: class C a where view :: a -> String instance C Char where view x = viewChar x -- primitive instance C String where view x = viewString x -- primitive instance C a => C [a] where view [] = "[]" view (x:xs) = concat $ [ "[", view x ] ++ concatMap (\y -> [ ", ", view y ]) xs ++ [ "]" ] would compile to this "core": view :: Type a -> a -> String view t = typecase t of Char -> viewInstanceChar [t'] -> typecase t' of Char -> viewInstanceString _ -> viewInstanceList t' _ -> error ("no instance for View " ++ show t) viewInstanceChar x = viewChar x viewInstanceString x = viewString x viewInstanceList t x = concat $ [ "[", view t x ] ++ concatMap (\y -> [ ", ", view t y ]) xs ++ [ "]" ] I think at least one Haskell compiler implements typeclasses this way. One nice property of this solution is that you retain parametricity, at least unless you also include "typeof"; any function with a typeclass constraint takes an additional type argument. Passing "Type a" around in this way is like using Typeable in current Haskell except it would have to be supported at the compiler level. And of course all of the standard optimizations apply, so if you statically know that the type is [Char] you inline "view" and get the correct answer. If you statically know that the type is [x] but you don't know x, you can still specialize the case down to just the typecase on the inside of the list. The biggest wart is that "view" is not a total function; the compiler needs to be extra careful to only call it on types that are instances of "View". I wonder if there is a good way to solve this problem? -- ryan
The biggest wart is that "view" is not a total function; the compiler needs to be extra careful to only call it on types that are instances of "View". I wonder if there is a good way to solve this problem?
The usual way to solve this is to define a data type corresponding to all the types in your class. For example: data Data a where | CHAR : Data Char | STRING : Data String | LIST : Data a -> Data [a] ... With this representation you no longer need typecase (which is horrendous semantic hack) and your dispatch function can be made total. Hope this helps, Wouter This message has been checked for viruses but the contents of an attachment may still contain software viruses, which could damage your computer system: you are advised to perform your own checks. Email communications with the University of Nottingham may be monitored as permitted by UK legislation.
Interesting. But from a practical point of view (I know, irrelevant! *grin*), it's very tempting to piggyback the entirety of the typeclass feature on a simple technique like this. It also gives you the benefit that functions like this:
manyConstraints :: (Show a, Eq a, Data a) => a -> Bool end up only taking a single "type" argument, instead of one per class.
In addition, when you use types directly you can remove arguments that are determined by FDs:
class Elem c e | c -> e where ... instance Elem [a] a where ...
toList :: Elem c e => c -> [e] toList = ...
turns into something like
toList :: Type c -> c -> [e] using e = fdElem c fdElem c = typecase c of [e] -> e ... other instances ...
(without the dependency, you have to pass both the type of c and e to toList.) -- ryan On Tue, Dec 9, 2008 at 1:36 AM, Wouter Swierstra <wss@cs.nott.ac.uk> wrote:
The biggest wart is that "view" is not a total function; the compiler needs to be extra careful to only call it on types that are instances of "View". I wonder if there is a good way to solve this problem?
The usual way to solve this is to define a data type corresponding to all the types in your class. For example:
data Data a where | CHAR : Data Char | STRING : Data String | LIST : Data a -> Data [a] ...
With this representation you no longer need typecase (which is horrendous semantic hack) and your dispatch function can be made total. Hope this helps,
Wouter
This message has been checked for viruses but the contents of an attachment may still contain software viruses, which could damage your computer system: you are advised to perform your own checks. Email communications with the University of Nottingham may be monitored as permitted by UK legislation.
On Fri, 2008-12-05 at 01:27 +0300, Dmitri O.Kondratiev wrote:
I am trying to define instance Show[MyType] so show (x:xs :: MyType) would return a single string where substrings corresponding to list elements will be separated by "\n". This would allow pretty printing of MyType list in several lines instead of one, as default Show does for lists.
For example:
data ShipInfo = Ship { name :: String, kind :: String, canons :: Int } deriving Show
s1 = Ship {name ="HMS Fly", kind = "sloop", canons=16} s2 = Ship {name ="HMS Surprise", kind = "frigate", canons=42}
-- Yet when I try to define: instance (Show ShipInfo) => Show [ShipInfo] where show (x:xs) = "<" ++ show x ++ ">" ++ show xs
The context on this is borked: you already know Show ShipInfo, so you don't need to assume it here.
-- I get this error: Illegal instance declaration for `Show [ShipInfo]' (The instance type must be of form (T a b c) where T is not a synonym, and a,b,c are distinct type variables)
Read this error again. Your instance is for the type `[] ShipInfo', which does not have the form GHC listed for you. The instance in GHC.Show (don't import it from there! Import it from Prelude, instead) is for a type of the form `[] a', which does have that form. Now, in this case, you don't need to define Show ([ShipInfo]), because the instance for Show [a] already does what you want; you just need to define an explicit instance for Show ShipInfo and over-ride the showList method. If you really, really wanted to define Show [ShipInfo], then putting {-# LANGUAGE FlexibleInstances, OverlappingInstances #-} at the beginning of your file would work. At the cost of using overlapping instances, of course. jcc
If you really, really wanted to define Show [ShipInfo], then putting
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
at the beginning of your file would work. At the cost of using overlapping instances, of course.
And at the cost of causing code like this:
f :: Show a => [a] -> String f xs = show xs
to fail to compile (see "Incoherent Instances"). Implement "showList"; it's the Right Answer for this case.
On Thu, 2008-12-04 at 14:46 -0800, Ryan Ingram wrote:
If you really, really wanted to define Show [ShipInfo], then putting
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
at the beginning of your file would work. At the cost of using overlapping instances, of course.
And at the cost of causing code like this:
f :: Show a => [a] -> String f xs = show xs
to fail to compile (see "Incoherent Instances").
Right.
Implement "showList"; it's the Right Answer for this case.
Yeah. jcc
Dmitri O.Kondratiev wrote:
I am trying to define instance Show[MyType] so show (x:xs :: MyType) would return a single string where substrings corresponding to list elements will be separated by "\n". This would allow pretty printing of MyType list in several lines instead of one, as default Show does for lists.
You're doing it wrong. Show is not for pretty-printing. Show is for the production of haskell syntax for debugging and copy-pasting into test cases, as well as for use with 'Read'. If you want to pretty print, use a different function name. Jules
Jules Bean schrieb:
Dmitri O.Kondratiev wrote:
I am trying to define instance Show[MyType] so show (x:xs :: MyType) would return a single string where substrings corresponding to list elements will be separated by "\n". This would allow pretty printing of MyType list in several lines instead of one, as default Show does for lists.
You're doing it wrong.
Show is not for pretty-printing.
(+1)
Show is for the production of haskell syntax for debugging and copy-pasting into test cases, as well as for use with 'Read'.
If you want to pretty print, use a different function name.
Maybe related: http://www.haskell.org/haskellwiki/List_instance
participants (9)
-
Daniel Fischer -
Dmitri O.Kondratiev -
Fraser Wilson -
Henning Thielemann -
Jonathan Cast -
Jules Bean -
Martijn van Steenbergen -
Ryan Ingram -
Wouter Swierstra