mwaddoups
mwaddoups

Reputation: 51

How to apply a function to one element in a heterogeneous list?

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

Answers (1)

Cirdec
Cirdec

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 showing 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

Related Questions