Traversable typeclass的用途 [英] The purpose of the Traversable typeclass
问题描述
有人可以向我解释,类型类型 Traversable
的用途是什么?
类型类定义是:
class(Functor t,Foldable t)=> Traversable(t :: * - > *)其中
所以 / code>是
Functor t
和可折叠t
。
$ b $ 遍历
函数是 Traversable
的成员,并且有以下签名:
traverse :: Applicative f => (a - > f b) - > t a - > f(t b)
为什么结果必须包含到应用程序中?它有什么意义?
我有以下示例:
module ExercisesTraversable where
import Test.QuickCheck(任意,任意)
import Test.QuickCheck.Checkers(quickBatch,eq,(= - =),EqProp)
import Test.QuickCheck.Classes(遍历)
类型TI = []
新类型IdentityT a = IdentityT a
派生(Eq,Ord,Show)
实例Functor IdentityT其中
fmap f(IdentityT a)= IdentityT(fa)
实例可折叠IdentityT其中
foldMap f(IdentityT a)= fa
instance可穿透的IdentityT,其中
遍历f(IdentityT a)= IdentityT< $> f a
实例任意a =>任意(IdentityT a)其中
任意=做
a< - 任意
返回(IdentityT a)
实例Eq a => EqProp(IdentityT a)其中(= - =)= eq
main = do
let trigger = undefined :: TI(Int,Int,[Int])
quickBatch可以遍历的触发器)
让我们来看看遍历
执行:
遍历f(IdentityT a)= IdentityT< $> fa
应用程序的结果类型 fa
必须是一个应用程序,为什么?请问一个仿函数不够吗?
身份一个不好的例子,因为它总是只包含一个值。你是对的 - 在这种情况下, Functor f
约束就足够了。但很显然,大部分遍历的结构并不是那么简单。
-
Functor t
都更强大,它还允许您访问/修改所有元素并重构结构,但只能完全独立于彼此(从而允许选择任意的计算顺序,在任何元素已被(懒惰地)映射到所有元素之前,将结果返回给结构,等等)。 -
可折叠t
,它使元素以线性顺序排列,但不重构结构。基本上,Foldable
就是可以降级到一个简单列表的容器类,如
toList :: Foldable t => t a - > [a]
...或通过
$连接任何monoidal类型b
$ bfoldMap ::(可折叠t,Monoid m)=> (a - > m) - > t a - > m
这里,每个元素的操作结果都是通过组合的(或者,如果没有元素,结果是
mempty
)。
在遍历
的情况下, Applicative f
约束基本上解除了这个monoid组合到你可以重建结构的东西。这些信件是:
mempty :: m
pure mempty :: fm
和
(<>) :: m - > m - > m
liftA2(<>):: f m - > f m - > fm
...但是另外,因为 f
也是一个函数,你可以将本地结果包装在任何数据构造函数中,因此不仅构建一个通用列表类的东西,而且构建一个任意的容器,包括一个具有原始结构的容器。
Could someone please explain to me, what is the purpose of the typeclass Traversable
?
The typeclass definition is:
class (Functor t, Foldable t) => Traversable (t :: * -> *) where
So Traversable
is a Functor t
and Foldable t
.
The traverse
function is a member of Traversable
and has the following signature:
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
Why does the result have to be wrapped into an applicative? What is the sense of it?
I have the following example:
module ExercisesTraversable where
import Test.QuickCheck (Arbitrary, arbitrary)
import Test.QuickCheck.Checkers (quickBatch, eq, (=-=), EqProp)
import Test.QuickCheck.Classes (traversable)
type TI = []
newtype IdentityT a = IdentityT a
deriving (Eq, Ord, Show)
instance Functor IdentityT where
fmap f (IdentityT a) = IdentityT (f a)
instance Foldable IdentityT where
foldMap f (IdentityT a) = f a
instance Traversable IdentityT where
traverse f (IdentityT a) = IdentityT <$> f a
instance Arbitrary a => Arbitrary (IdentityT a) where
arbitrary = do
a <- arbitrary
return (IdentityT a)
instance Eq a => EqProp (IdentityT a) where (=-=) = eq
main = do
let trigger = undefined :: TI (Int, Int, [Int])
quickBatch (traversable trigger)
Let's take a look at the traverse
implementation:
traverse f (IdentityT a) = IdentityT <$> f a
The result type of the application f a
has to be an applicative, why? Would a functor not be enough?
Identity
is a bit of a poor example as it always contains exactly one value. You're right – in this case, a Functor f
constraint would be sufficient. But clearly, most traversables aren't so structurally trivial.
What traverse
does is: it "visits", in some well-specified order, all elements in a container, performs some operation on them, and reconstructs the structure as it was. This is more powerful than either
Functor t
, which also allows you to visit/modify all elements and reconstructs the structure, but only completely independent of one another (thus allowing to choose an arbitrary order-of-computation, returning a thunk to the structure before any of the elements have been (lazily) mapped at all, etc.).Foldable t
, which brings the elements in a linear order, but does not reconstruct the structure. Basically,Foldable
is just the class of containers that can be demoted to a simple list, as witnessed bytoList :: Foldable t => t a -> [a]
...or to a concatenation of any monoidal type, via
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
Here, the results of the operation on each element are combined through the monoid operation (or, in case the are no elements, the result is
mempty
).
In case of traverse
, the Applicative f
constraint basically lifts this monoid-combining to something in which you can also reconstruct the structure. The correspondence is
mempty :: m
pure mempty :: f m
and
(<>) :: m -> m -> m
liftA2 (<>) :: f m -> f m -> f m
...but in addition, because f
is also a functor, you can wrap the local results in any data constructor and thus build not only a generic list-like thing but an arbitrary container, including one with the original structure.
这篇关于Traversable typeclass的用途的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!