【问题标题】:Testing Haskell traversable with a simple example用一个简单的例子测试 Haskell 可遍历
【发布时间】:2014-02-16 00:17:17
【问题描述】:

我正在尝试使用 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 中的所有项目,逐个成员并使用 show。我是否走在正确的轨道上?应该如何实现可遍历的实例化?

【问题讨论】:

    标签: haskell functor traversable


    【解决方案1】:

    我只会使用语言扩展来为我派生它:

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

    【讨论】:

    • 谢谢,这解决了添加更多成员时的问题,以及我在@rampion 的答案下方的 cmets 中提出的问题。
    【解决方案2】:

    这里典型的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
      

      而如果DataTraversable 的一个实例,那么我们会将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 一个有效的定义:

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

      接下来,您的 main 函数中出现错误。你写的是Tr.traverse (putStrLn show) exampleData,而你的意思是Tr.traverse (putStrLn . show) exampleData

      putStrLnString -&gt; IO () 类型,所以putStrLn show 需要show 作为字符串才能进行类型检查,但show :: Show a -&gt; a -&gt; 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

      【讨论】:

      • 感谢您的好回答。是否可以进一步概括这一点,以便我可以在数据结构中添加成员而不在 traverse 函数中添加参数?现在只支持本例中的两个成员。
      猜你喜欢
      • 1970-01-01
      • 2019-12-11
      • 1970-01-01
      • 1970-01-01
      • 2017-09-29
      • 1970-01-01
      • 2018-01-24
      • 2012-11-06
      • 2023-03-27
      相关资源
      最近更新 更多