Reputation: 51
So I'm trying to work out the most haskell-ish way to do the following (as still a reasonable beginner):
You have a heterogeneous list with different types inside. In my code I'm using Show
. What I want to achieve is to do something to one element in this list as well after showing it. Currently you would write (naively)
[show a, show b, (++) " " $ show c, show d, show e]
Is there some way to pack the function (++) " "
with the value c
so you could write something like
map show [a, b, inverseShow ((++) " ") c, d, e] -- not real haskell
or is this not possible in the language?
I am aware you can use existential quantifiers to get part way there
data ShowBox = forall s. Show s => SB s
instance Show ShowBox where show (SB s) = show s
map show [SB a, SB b, SB c, SB d, SB e]
but I'm struggling to see how to move from this to my desired functionality in the 'best practice' way?
EDIT: After reading the comments below, I've pretty much come to the conclusion this is not the best way to write the code. Below is an implementation of the sort of behaviour I wanted:
{-# LANGUAGE ExistentialQuantification #-}
data ShowAndTell = forall s. Show s => STell s (String -> String)
instance Show ShowAndTell where show (STell s f) = f $ (show s)
st :: (Show a) => a -> ShowAndTell
st a = STell a id
addSpace :: String -> String
addSpace x = x ++ " "
main = (mapM_ putStrLn) . (map show) $ [st "1", st 3, STell 5.5 addSpace]
This outputs "1"
,3
,5.5
but could be described as rather ugly!
Upvotes: 0
Views: 171
Reputation: 24156
If you have 5 items, a
, b
, c
, d
, and e
and the only thing you know about them is that they have Show
instances, you can put everything† you know about them in a list by show
ing them before you put them in the list.
items :: [String]
items = [show a, show b, show c, show d, show e]
If you want to do different things to the different elements of the list, like add a space before the third one, you can make a list of the different things to do.
changes :: [String -> String]
changes = [id, id, (++) " ", id, id]
Then apply the changes
to the items
in the list by zipping the two lists together, applying (with $
) the change to the corresponding item.
changed :: [String]
changed = zipWith ($) changes items
†Techically Show
has two other methods than show
which would be captured by existential quantification, showsPrec
and showList :: [a] -> ShowS
. An existentially qualified showList
is completely useless, the only thing you can do with it is repeat the same element over and over again (showList []
, showList [x]
, showList[x,x]
, etc) because you can't prove that any other items have the same type to be put in a list with it. If you need showsPrec
you don't need show
because show = ($ "") . showsPrec 0
.
Upvotes: 8