我有类型:
data Cons = Cons {myprop :: String}
稍后我正在映射列表,将属性设置为不同的值:
fmap (\x -> x{myprop = ""}) mylist
是否有点免费方式来表达此匿名函数?
答案 0 :(得分:7)
这样做没有免费的方法,但您可以使用lens库(附带一大堆好吃的东西):
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
-- If you put a _ at the beginning of the field name lens can derive the methods for you
data Cons = Cons { _myprop :: String } deriving (Eq, Show)
-- Derives the myprop lens for you (works for all fields in the record)
makeLenses ''Cons
-- You can do this manually as
-- myprop :: Functor f => (String -> f String) -> Cons -> f Cons
-- myprop = lens _myprop (\c newProp -> c { _myprop = newProp })
-- This actually lets you define the lenses for your type without depending on lens
-- which is a bigger deal than you might think. You can fully support a library and
-- and its paradigms without depending on it at all.
main = do
let cs = map Cons ["a", "b", "c"]
-- With the prefix set function
test1 = fmap (set myprop "") cs
-- Or with an operator
test2 = fmap (myprop .~ "") cs
print cs
print test1
print test2
data Email = Email
{ _emailAccount :: String
, _emailDomain :: String
} deriving (Eq, Show)
makeLenses ''Email
data Person = Person
{ _personName :: String
, _personAge :: Int
, _personEmail :: Email
} deriving (Eq, Show)
makeLenses ''Person
testPeople :: [Person]
testPeople = [
Person "A" 40 (Email "aaa" "gmail.com"),
Person "B" 45 (Email "bbb" "email.com"),
Person "C" 50 (Email "ccc" "outlook.com")]
domains :: [Person] -> [String]
domains ps = ps^..traverse.personEmail.emailDomain
statefulFun :: MonadIO m => StateT Person m ()
statefulFun = do
liftIO $ putStrLn "Changing the name"
personName .= "a different name"
liftIO $ putStrLn "The name is changed!"
personEmail.emailAccount %= map toUpper
moreState :: MonadIO m => StateT Person m ()
moreState = do
personName .= "Foo"
zoom personEmail $ do
current <- get
liftIO $ putStr "Current email is: "
liftIO $ print current
emailAccount .= "foo"
emailDomain .= "foo.com"
main :: IO ()
main = do
finalState <- execStateT (moreState >> statefulFun) (head testPeople)
print finalState
正如你所看到的,镜头看起来像是倒退(它们实际上并不是,因为它们有一种更通用的类型可以让它们做疯狂的事情)。有很好的遍历复杂数据结构的方法,可选地执行效果,以及大量运算符,用于编写非常强制性的状态代码,看起来像是使用点进行正常的OOP方法访问。一旦你开始理解它们,它可以让你轻松地在大型复杂数据结构上抽象出常见和复杂的模式!
答案 1 :(得分:2)
您可能想要查看镜头....镜头的一个要点是让您更改记录中的单个值,但它们比您的示例更进一步,允许您更改记录中的单个值嵌套在记录中。
例如,请参阅Control.Lens。