Тестирование пройденного Haskell на простом примере
Я пытаюсь обойти всех членов структуры данных в haskell, используя Data.Traversable, который задокументирован по следующим ссылкам:
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, член за членом и с помощью шоу. Нахожусь ли я на правильном пути, и как должен быть реализован обходной экземпляр?
3 ответа
Я бы просто использовал языковые расширения, чтобы получить его для меня:
{-# 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)
Типичный Traversable
реализация здесь будет
traverse f (Data foo bar) = Data <$> f foo <*> f bar
Итак, ваше определение 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
,
putStrLn
имеет тип String -> IO ()
, так putStrLn show
необходимый show
быть строкой для проверки типов, но 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
,