我们知道,fmap
的签名是(a -> b) -> f a -> f b
,其中f
是Functor
。
[似乎很自然,应该尽可能地通用,并且要更好地分解代码,一个人可能希望将“事物列表”映射到另一个可能不同的“事物列表”。凭直觉,我不明白为什么它不应该或不可能。
[我正在寻找的是功能gmap
,其功能与fmap
相同,但是具有该签名gmap :: (a -> b) -> (f a) -> (g b)
,在这里我允许到达和离开的容器有所不同。
我不确定f
和g
为Functors
的一般情况是否有意义,但是Traversable
类听起来更像是“事物列表”的概念,假设我对迭代数据最感兴趣。
所以签名应该是gmap :: (Traversable f, Traversable g) => (a -> b) -> (f a) -> (g b)
。
即使g
与f
的性质不同,它仍然可以从左到右遍历,因此仍然觉得应该可以将f
的第k个被访问元素映射到g
的第k个被访问元素。
假设我的想法没有错,Haskell中是否有这样的功能?
本质上,我的问题是,您将如何以最简洁明了的方式从Haskell中的类似列表的事物转换为另一种?
您可以创建自己的类型类,并提供一种从一个函子转换为另一个函子的方法,这是将列表转换为Tree的一种方法的示例,但是您可以使用任何您认为正确的方法来解决您的问题。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
class (Functor f, Functor g) => GFunctor f g where
toG :: f a -> g a
gmap :: (a -> b) -> (f a) -> (g b)
gmap fn functor = toG $ fmap fn functor
data Tree a = Leaf | Node (Tree a) a (Tree a) deriving Show
instance Functor Tree where
fmap f Leaf = Leaf
fmap f (Node t1 x t2) = Node (fmap f t1) (f x) (fmap f t2)
instance GFunctor [] Tree where
toG [] = Leaf
toG [x] = Node Leaf x Leaf
toG (x:xs) = Node (toG $ (takeHalf xs)) x ((toG $ dropHald xs))
takeHalf xs = take ((length xs) `div` 2) xs
dropHald xs = drop ((length xs) `div` 2) xs
res :: Tree Int
res = gmap (+1) [1,2,3,4,5]
输出:
res
=> Node (Node Leaf 3 (Node Leaf 4 Leaf)) 2 (Node Leaf 5 (Node Leaf 6 Leaf))
我认为您可以通过fmap
和自然变换来做到这一点。这是一个使用natural-transformations包的示例,目的只是为了演示这个想法。
我想到的第一个自然转变是safeHead
:
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just x
使用natural-transformations
软件包运行GHCi:
Prelude Control.Natural> t = wrapNT safeHead
Prelude Control.Natural> t # [1..3]
Just 1
Prelude Control.Natural> t # []
Nothing
Prelude Control.Natural> fmap show (t # [1..3])
Just "1"
Prelude Control.Natural> fmap show (t # [])
Nothing
所以,对于自然变换t
,像fmap f . (t #)
这样的事情会做你想要的吗?
((我在想f
是正常函数a -> b
。]