У меня есть тип:
data Cons = Cons {myprop :: String}
а позже я сопоставляю список, устанавливая для свойства другое значение:
fmap (\x -> x{myprop = ""}) mylist
Есть ли бесточечный способ выразить эту анонимную функцию?
У меня есть тип:
data Cons = Cons {myprop :: String}
а позже я сопоставляю список, устанавливая для свойства другое значение:
fmap (\x -> x{myprop = ""}) mylist
Есть ли бесточечный способ выразить эту анонимную функцию?
Бесточечного способа сделать это не существует, но вместо этого вы можете использовать библиотеку 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
Как вы можете видеть, линзы выглядят как композиция в обратном порядке (на самом деле это не так, потому что у них более общий тип, который позволяет им делать сумасшедшие вещи). Есть хорошие способы обхода сложных структур данных, опциональное выполнение эффектов по мере продвижения и множество операторов для написания очень императивного кода с отслеживанием состояния с тем, что кажется обычным доступом к методу ООП с точкой. Это позволяет вам абстрагировать общие и сложные шаблоны от больших и сложных структур данных с небольшими усилиями, как только вы хотя бы начнете их вникать!
Возможно, вы захотите взглянуть на линзы.... Одна из точек линз - позволить вам изменить одно значение в записи, но они идут намного дальше вашего примера, позволяя вам изменять одно значение в записях, вложенных в записи .
См., например, Control.Lens.