2014-01-24 68 views
4

我試圖用Data.Traversable,這是在下面的網址記錄遍歷Haskell中的數據結構中的所有成員:測試哈斯克爾穿越一個簡單的例子

http://hackage.haskell.org/package/base-4.6.0.1/docs/Data-Traversable.html http://www.haskell.org/haskellwiki/Foldable_and_Traversable

到目前爲止,我已經拿出了下面的代碼,據我所知,錯過了Tr.Traversable實例的正確實現。

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 

我試圖在exampleData,member by member和show中打印所有項目。我在正確的軌道上,應該如何實施可穿越的實例?

回答

4

我只想用語言擴展,以獲得對我來說:

{-# 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 
() 

如果你想知道如何編譯器自動實現它,你可以與-ddump-deriv標誌編譯(清理了一下):

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) 
+0

謝謝,在添加更多成員時解決了問題,以及我在@rampion回答下面的評論中提問。 – toeplitz

3

這裏的典型Traversable實施將

traverse f (Data foo bar) = Data <$> f foo <*> f bar 
3

所以你的traverse定義不與給定的簽名統一。

traverse f = Data f -- f :: x implies Data f :: x -> Data x, so this implies 
traverse :: x -> x -> Data x 

而如果Data是的Traversable一個實例,然後我們會專注Tr.traverse

Tr.traverse :: Applicative f => (a -> f b) -> Data a -> f (Data b) 

試圖統一它們給人的問題:

traverse ~ Tr.traverse if and only if 
    x ~ (a -> f b) 
    x ~ Data a 
    Data x ~ f (Data b) 

這就是爲什麼編譯器會抱怨:

Couldn't match expected type `Data a' with actual type `a -> f b' 
In the first argument of `Data', namely `f' 
In the expression: Data f 
In an equation for `traverse': traverse f = Data f 

讓我們給出一個有效的定義橫向:

traverse f (Data a a') = Data <$> f a <*> f a' 

接下來,您必須在您的主功能的錯誤。當你的意思是Tr.traverse (putStrLn . show) exampleData時,你寫了Tr.traverse (putStrLn show) exampleData

putStrLnString -> IO()類型,因此需要putStrLn showshow是爲了類型檢查,但show :: Show a -> a -> String一個字符串。 這就是爲什麼編譯器會抱怨:

Couldn't match expected type `Test -> IO b0' 
      with actual type `IO()' 
In the return type of a call of `putStrLn' 
Probable cause: `putStrLn' is applied to too many arguments 
In the first argument of `Tr.traverse', namely `(putStrLn show)' 
In a stmt of a 'do' block: Tr.traverse (putStrLn show) exampleData 

Couldn't match type `a0 -> String' with `[Char]' 
Expected type: String 
    Actual type: a0 -> String 
In the first argument of `putStrLn', namely `show' 
In the first argument of `Tr.traverse', namely `(putStrLn show)' 
In a stmt of a 'do' block: Tr.traverse (putStrLn show) exampleData 

您想使用.運營商組成的功能:

putStrLn . show == \a -> putStrLn (show a) 

你也可以只使用print其定義爲putStrLn . show

+0

謝謝你的一個很好的答案。是否有可能進一步概括這一點,以便我可以在數據結構中添加成員而無需在遍歷函數中添加參數?這個例子現在只支持這兩個成員。 – toeplitz