最近我不得不写下面这个函数:
mToL :: Maybe [a] -> [Maybe a]
mToL Nothing = []
mToL (Just xs) = map Just xs
这就提出了一个问题:是否有可能将上述内容概括为:
transposeF :: (Functor f, Functor g) => f (g a) -> g (f a)
我想这只有在将f (g a)
“崩溃”到f a
时才有效,或者还有其他办法吗?
答案 0 :(得分:13)
The Traversable
typeclass提供了sequence
和sequenceA
操作,这些操作为您的问题提供了最常用的解决方案,但它们需要不同的约束:
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
答案 1 :(得分:10)
这一般是不可能的,不。将f
设为Const b
,将g
设为Identity
。
newtype Const b a = Const { getConst :: b }
newtype Identity a = Identity { runIdentity :: a }
这些都是具有正常实例的显然算子。 transposeF
无法正常工作,因为Const b
不提供用于包装Identity的任何a
。所以你不能写转置函数。
另一方面,对于很多Functor配对来说这很好。分类概念是adjoint functor的概念,一旦你看到它们,你就会开始到处看到它们。它们本身就是一个非常强大的概念。
答案 2 :(得分:8)
实际上,有一个类型类可以支持这个。它不包含在标准类型类中,但是"可表示的仿函数"有这种能力。
代表性仿函数是一个带有两件事的仿函数F
A
F
和(->) A
我们可以将其表示为
type family ReprObj (f :: * -> *):: *
class Functor f => Repr f where
toHom :: f a -> (ReprObj f -> a)
fromHom :: (ReprObj f -> a) -> f a
其中toHom . fromHom = fromHom . toHom = id
。可表示的仿函数的一个例子,
newtype Pair a = Pair (a, a) deriving Functor
type instance ReprObj Pair = Bool
instance Repr Pair where
toHom (Pair (a, b)) True = a
toHom (Pair (a, b)) False = b
fromHom f = Pair (f True, f False)
现在使用这个我们可以派生
swap :: (Functor f, Functor g, Repr f, Repr g) => f (g a) -> g (f a)
swap g = fromHom $ \obj -> fmap ($ obj) hom
where hom = fmap toHom g
事实上,我们也可以从可表示的仿函数中获得一个免费的applicative和monad实例。我详细介绍了如何在blog post中完成此操作。
答案 3 :(得分:0)
使用仿函数是不可能的,因为没有通用的方法来展开仿函数值:
Prelude> :info Functor
class Functor f where
fmap :: (a -> b) -> f a -> f b
(GHC.Base.<$) :: a -> f b -> f a
-- Defined in `GHC.Base'
instance Functor Maybe -- Defined in `Data.Maybe'
instance Functor (Either a) -- Defined in `Data.Either'
instance Functor [] -- Defined in `GHC.Base'
instance Functor IO -- Defined in `GHC.Base'
instance Functor ((->) r) -- Defined in `GHC.Base'
instance Functor ((,) a) -- Defined in `GHC.Base'
相反,您可以创建自己的类型类以提供通用transpose
函数,如下所示:
import Control.Applicative
class Applicative f => Transposable t where
unwrap :: t a -> a
transpose :: s (t a) -> t (s a)
transpose = fmap pure . unwrap
我们Applicative
为Transposable
超类的原因是我们可以使用其pure
方法。 Applicative
的所有实例也是Functor
的实例。