繁体   English   中英

在Haskell中有没有办法表达一个点自由函数来编辑数据类型的属性?

[英]Is there a way in Haskell to express a point free function to edit the property of a data type?

我有类型:

data Cons = Cons {myprop :: String}

然后在我映射列表,将属性设置为不同的值:

fmap (\\x -> x{myprop = ""}) mylist

是否有一种免费的方式来表达这个匿名函数?

没有一种免费的方法可以做到这一点,但你可以使用镜头库(附带一大堆好东西):

{-# 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方法访问带有点的非常有用的状态代码。 一旦你开始至少弄清楚它们,它可以让你轻松地抽象大型复杂数据结构上的常见和复杂模式!

您可能想要查看镜头......镜头的一个要点是让您更改记录中的单个值,但它们比您的示例更进一步,允许您更改嵌套在记录中的记录中的单个值。

例如,请参阅Control.Lens。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM