I have the type:
data Cons = Cons {myprop :: String}
and later on I'm mapping over a list, setting the property to a different value:
fmap (\x -> x{myprop = ""}) mylist
Is there a point free way to express this anonymous function?
There isn't a point free way of doing this, but you can instead use the lens library (which comes with a whole basket of goodies):
{-# 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
That "basket of goodies" that comes with lenses contains things like
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
As you can see, lenses look like the compose backwards (they actually don't, it's because they have a more general type that lets them do crazy things). There's nice ways of traversing complex data structures, optionally performing effects as you go, and plenty of operators for writing very imperative looking stateful code with what appears to be normal OOP method access with a dot. It lets you abstract common and complex patterns over large and complex data structures with little effort, once you start to grok them at least!