I am trying to traverse all members of a data structure in haskell using Data.Traversable, which is documented at the following urls:
http://hackage.haskell.org/package/base-4.6.0.1/docs/Data-Traversable.html http://www.haskell.org/haskellwiki/Foldable_and_Traversable
So far I have come up with the following code which is as far as I know missing a proper implementation of the Tr.Traversable instancing.
import qualified Data.Traversable as Tr
import qualified Data.Foldable as Fl
import Control.Monad
import Control.Applicative
data Test = Test { desc :: String
, value :: Int
}
data Data t = Data { foo :: t
, bar :: t
}
exampleData = Data { foo = Test "Foo" 1
, bar = Test "Bar" 2
}
instance Show Test where
show f = (desc f) ++ ": " ++ (show $ value f)
instance (Show a) => Show (Data a) where
show f = show (foo f)
instance Functor Data where
fmap = Tr.fmapDefault
instance Fl.Foldable Data where
foldMap = Tr.foldMapDefault
instance Tr.Traversable Data where
traverse f = Data f -- Try to show a Test entry inside the Data structure
--
-- traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
--
main = do
putStrLn $ show exampleData
Tr.traverse (putStrLn show) exampleData
I am trying to print all the items in exampleData, member by member and using show. Am I on the right track and how should the traversable instancing be implemented?
A simple example of its usage is using a list as the traversable structure, and IO as the applicative: λ> import Data.Traversable λ> let qs = ["name", "quest", "favorite color"] λ> traverse ( hing -> putStrLn ("What is your " ++ thing ++ "?") *> getLine) qs What is your name?
The Traversable instance is almost the same, except the constructors are called in applicative style. This means that we can have (side-)effects while rebuilding the tree. Applicative is almost the same as monads, except that effects cannot depend on previous results.
With the Traversable typeclass, you could probably write your algorithms more independent and versatile. But my experience says, that Traversable is usually only used to simply glue algorithms to existing data structures. It is quite nice not to need to write similar functions for different datatypes qualified, too.
So traverse takes some structure, and applies f to transform every element in the structure into some applicative, it then sequences up the effects of those applicatives from left to right, returning a structure with the same shape containing the results. You can also compare it to Foldable, which defines the related function traverse_.
The typical Traversable
implementation here would be
traverse f (Data foo bar) = Data <$> f foo <*> f bar
I would just use the language extensions to derive it for me:
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveFoldable #-}
import qualified Data.Traversable as Tr
import qualified Data.Foldable as Fl
import Control.Monad
import Control.Applicative
data Test = Test { desc :: String
, value :: Int }
data Data t = Data { foo :: t
, bar :: t } deriving (Functor, Tr.Traversable, Fl.Foldable)
exampleData = Data { foo = Test "Foo" 1
, bar = Test "Bar" 2
}
instance Show Test where
show f = (desc f) ++ ": " ++ (show $ value f)
instance (Show a) => Show (Data a) where
show f = show (foo f)
main = do
putStrLn $ show exampleData
Tr.traverse (putStrLn . show) exampleData
> runhaskell test_traversable.hs
Foo: 1
Foo: 1
Bar: 2
()
If you want to know how the compiler automatically implements it, you could compile it with the -ddump-deriv
flag (cleaned up a bit):
instance Functor Data where
fmap f (Data foo' bar') = Data (f foo') (f bar')
instance Tr.Traversable Data where
tr.traverse f (Data foo' bar') = Data <$> (f foo') <*> (f bar')
instance Fl.Foldable Data where
Fl.foldr f z (Data foo' bar') = f foo' (f bar' z)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With