我刚在Control.Applicative
的文档中发现了Const
,但是我很难理解它在哪些情况下有用,相比直接使用Monoid
。
我错过了什么吗?
我刚在Control.Applicative
的文档中发现了Const
,但是我很难理解它在哪些情况下有用,相比直接使用Monoid
。
我错过了什么吗?
与Traversable
结合使用时非常有用。
getConst . traverse Const :: (Monoid a, Traversable f) => f a -> a
这就是将一堆东西黏在一起的通用方法。这是我被说服将 Applicative
与 Monad
分开的用例之一。我需要像广义的 elem
这样的东西。
elem :: Eq x => x -> Term x -> Bool
我希望为一个参数化自由变量表示的可遍历术语(Traversable Term)
执行出现检查。我一直在更改Term
的表示方式,厌倦了修改无数个遍历函数,其中一些是进行累加而不是有副作用的映射。我很高兴找到了一种同时涵盖这两个方面的抽象。
正如dave4420所提到的,实现Van Laarhoven lens的访问器和更新器涉及到Const
函子。更详细地说:
{-# LANGUAGE Rank2Types #-}
import Control.Applicative
import Control.Monad.Identity
-- The definition of Van Laarhoven lenses:
type Lens a b = forall f . Functor f => (b -> f b) -> (a -> f a)
-- Getter passes the Const functor to the lens:
get :: Lens a b -> a -> b
get l = getConst . (l Const)
-- Updater passes the Identity functor to the lens:
modify :: Lens a b -> (b -> b) -> (a -> a)
modify l f = runIdentity . l (Identity . f)
set :: Lens a b -> b -> (a -> a)
set l r = modify l (const r)
-- Example: -------------------------------------------
data Person = Person { _name :: String, _age :: Int }
deriving Show
name :: Lens Person String
name f (Person n a) = fmap (\x -> Person x a) (f n)
age :: Lens Person Int
age f (Person n a) = fmap (\x -> Person n x) (f a)
main :: IO ()
main = do
let john = Person "John" 34
print $ get age john
print $ set name "Pete" john
当你拥有适用于所有(Applicative
)Functor
的函数或数据结构,并希望以退化的方式重复使用它时,这将非常有用。这类似于向可以接受任意函数的函数传递const
或id
。
Van Laarhoven镜头是基于任意Functor定义的,并使用Const
来派生字段访问器(以及同样简单的Identity
来派生字段更新器)。
Traversable
类型,如pigworker所提到的,就是另一个例子。
对于遍历,您需要完整的 Applicative 行为,这就是 pigworker 在另一个答案中描述的内容。
foldMap id
不会产生更少的约束(使用Foldable
而不是Traversable
),但会产生相同的结果吗? - PetrFoldable
存在的原因。但是,一切Traversable
都是Foldable
,这非常有用,上面是foldMapDefault
的构造方式。插播一句:SHE支持DefaultSuperclassInstances
,因此通过默认静默方式,一切Traversable
都变成了Foldable
。 - pigworker