如何优雅地创建可选值列表?

问题描述 投票:3回答:3

考虑以下功能

annotate :: AST -> [MetaInfo]
annotate (ArgDecl name nameReq maybeVal) 
    = concat [
      [m [Name] name],
      if nameReq
        then [m [Operator] "!"]]
        else [],
      case maybeVal of
                      Just v  -> [annotate v]
                      Nothing -> []
    ]
...

这看起来很优雅。有没有比使用concat和空列表更好的方法来创建可能存在或不存在的值列表?

haskell
3个回答
3
投票

正如@AJFarmar所说,只是清理它会有所帮助:

annotate :: AST -> [MetaInfo]
annotate (ArgDecl name nameReq maybeVal)
  = concat
    [ [m [Name] name]
    , if nameReq
        then [m [Operator] "!"]
        else []
    , case maybeVal of
        Just v -> [annotate1 v]
        Nothing -> []
    ]

catMaybesData.Maybe的帮助下,也可以更清楚地从singleton / empty-set切换到Just / Nothing:

annotate' :: AST -> [MetaInfo]
annotate' (ArgDecl name nameReq maybeVal)
  = catMaybes
    [ Just (m [Name] name)
    , if nameReq
        then Just (m [Operator] "!")
        else Nothing
    , annotate1 <$> maybeVal
    ]

最后,如果您更喜欢这种语法,可以考虑创建一些辅助函数并使用<>

annotate'' :: AST -> [MetaInfo]
annotate'' (ArgDecl name nameReq maybeVal)
  =    always (m [Name] name)
    <> whenever nameReq (m [Operator] "!")
    <> sometimes (annotate1 <$> maybeVal)

有助手:

always :: a -> [a]
always = (:[])
sometimes :: Maybe a -> [a]
sometimes = maybeToList
whenever :: Bool -> a -> [a]
whenever b a = if b then [a] else []

3
投票

当我最终得到像这样的粗略代码时,我通常会通过命名碎片来清理它:

annotate :: AST -> [MetaInfo]
annotate (ArgDecl name nameReq maybeVal) 
    = concat [name, bang, annotation]
    where
        name = [m [Name] name]

        bang | nameReq = [m [Operator] "!"]]
             | otherwise = []

        annotation = case maybeVal of
          Just v  -> [annotate v]
          Nothing -> []

考虑到你有一个Maybe,我可能会去catMaybes简化annotation定义:

annotate :: AST -> [MetaInfo]
annotate (ArgDecl name nameReq maybeVal) 
    = catMaybes [name, bang, annotation]
    where
        name = Just $ m [Name] name

        bang | nameReq = Just $ m [Operator] "!"
             | otherwise = Nothing

        annotation = annotate <$> maybeVal

3
投票

我倾向于将列表推广到任何Alternative,并利用相关的函数。在这种情况下,我发现guard condition *> ...非常好。

annotate :: Alternative f => AST -> f MetaInfo
annotate (ArgDecl name nameReq maybeVal) =
   pure (m [Name] name)
   <|>
   guard nameReq *> pure (m [Operator] "!")
   <|>
   maybe empty (pure . annotate) maybeVal

或者,即使使用普通列表,使用列表推导看起来也不错。

annotate :: AST -> [MetaInfo]
annotate (ArgDecl name nameReq maybeVal) =
   [m [Name] name]
   ++
   [ m [Operator] "!" | nameReq ]
   ++
   [ annotate v | Just v <- maybeVal ]
© www.soinside.com 2019 - 2024. All rights reserved.