试图摆脱嵌套的表达式

 婷婷爱BB 发布于 2023-01-01 06:53

我有一个yaml文件:

base123:
  key1: "key1"
  key2: "key2"
  key3: "key3"

和代码,它被允许从中读取所有3个值:

read123 :: IO (String, String, String)
read123 = do
  rawConfig <- Y.decodeFile "config.yml" :: IO (Maybe Y.Value)
  case rawConfig of
    Just res1 ->
      case res1 of
        Object res2 ->
          case (LHashMap.lookup "base123" res2) of
            Just (Object res3) -> 
              case (LHashMap.lookup "key1" res3) of
                Just (String key1) -> 
                  case (LHashMap.lookup "key2" res3) of
                    Just (String key2) -> 
                      case (LHashMap.lookup "key3" res3) of
                        Just (String key3) -> return (key1, key2, key3)
        _ -> error "some error"        

    Nothing -> error "error123"

它似乎工作正常.但我相信,必须有一种方法可以在不使用Lens的情况下摆脱嵌套表达式.有没有?或者有什么方法可以更简单地做同样的事情?

更新:

  rawConfig <- Y.decodeFile "config.yml" :: IO (Maybe Y.Value)
  case rawConfig of
    Just (Object res1) -> LHashMap.lookup "base123" res1
    Nothing -> error "error"

  return ("", "", "") -- stub 

错误:

Couldn't match type `Maybe' with `IO'
    Expected type: IO Value
      Actual type: Maybe Value
    In the return type of a call of `LHashMap.lookup'

Sibi.. 6

我建议您使用正确的数据类型来存储您的YAML数据.Michael Snoyman的yaml库重用了大部分API aeson.所以它与你使用aeson包的方式非常相似.这是一个有效的示例代码:

{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative
import Control.Monad (mzero)
import Data.Text
import Data.Yaml

data Base = Base {
  key1 :: Text,
  key2 :: Text,
  key3 :: Text
  } deriving (Show)

instance FromJSON Base where
  parseJSON (Object v) = Base <$>
                         ((v .: "base123") >>= (.: "key1")) <*>
                         ((v .: "base123") >>= (.: "key2")) <*>
                         ((v .: "base123") >>= (.: "key3"))

  parseJSON _ = mzero

main = do
  b <- decodeFile "/home/sibi/yaml.yml" :: IO (Maybe Base)
  print b

在ghci:

?> main
Just (Base {key1 = "key1", key2 = "key2", key3 = "key3"})


Petr Pudlák.. 5

使用Maybemonad 可以简化这一过程.

让我们专注于片段

case (LHashMap.lookup "base123" res2) of
  Just (Object res3) -> 
    case (LHashMap.lookup "key1" res3) of
      Just (String key1) -> 
        case (LHashMap.lookup "key2" res3) of
          Just (String key2) -> 
            case (LHashMap.lookup "key3" res3) of
              Just (String key3) -> return (key1, key2, key3)

首先让我们定义提取器ObjectString(我不知道你需要的确切类型,但这应该是显而易见的):

getString :: (MonadPlus m) => ... -> m String
getString (String o) = return o
getString _          = mzero

getObject :: ...
getObject (Object o) = return o
getObject _          = mzero

现在可以用Maybemonad中的计算替换代码段:

do -- in the Maybe moned
  res3 <- getObject =<< LHashMap.lookup "base123" res2
  key1 <- getString =<< LHashMap.lookup "key1" res3
  key2 <- getString =<< LHashMap.lookup "key2" res3
  key3 <- getString =<< LHashMap.lookup "key2" res3
  return (key1, key2, key3)

如果一切顺利,你会得到Just (...),否则Nothing(你可以变成一个IO错误).


旁白:请注意,嵌套case表达式不会按预期工作,因为它们会形成不完整的模式.例如,如果您定义

test x y =
  case x of
    Just x' ->
      case y of
        Just y' -> True
    _ -> False

然后test (Just 0) Nothing失败了.该_模式仅适用于最外层case,而不适用于内部模式.

此外,我建议从部分中拆分纯部分(在纯函数中处理数据)IO.

2 个回答
  • 我建议您使用正确的数据类型来存储您的YAML数据.Michael Snoyman的yaml库重用了大部分API aeson.所以它与你使用aeson包的方式非常相似.这是一个有效的示例代码:

    {-# LANGUAGE OverloadedStrings #-}
    
    import Control.Applicative
    import Control.Monad (mzero)
    import Data.Text
    import Data.Yaml
    
    data Base = Base {
      key1 :: Text,
      key2 :: Text,
      key3 :: Text
      } deriving (Show)
    
    instance FromJSON Base where
      parseJSON (Object v) = Base <$>
                             ((v .: "base123") >>= (.: "key1")) <*>
                             ((v .: "base123") >>= (.: "key2")) <*>
                             ((v .: "base123") >>= (.: "key3"))
    
      parseJSON _ = mzero
    
    main = do
      b <- decodeFile "/home/sibi/yaml.yml" :: IO (Maybe Base)
      print b
    

    在ghci:

    ?> main
    Just (Base {key1 = "key1", key2 = "key2", key3 = "key3"})
    

    2023-01-01 07:08 回答
  • 使用Maybemonad 可以简化这一过程.

    让我们专注于片段

    case (LHashMap.lookup "base123" res2) of
      Just (Object res3) -> 
        case (LHashMap.lookup "key1" res3) of
          Just (String key1) -> 
            case (LHashMap.lookup "key2" res3) of
              Just (String key2) -> 
                case (LHashMap.lookup "key3" res3) of
                  Just (String key3) -> return (key1, key2, key3)
    

    首先让我们定义提取器ObjectString(我不知道你需要的确切类型,但这应该是显而易见的):

    getString :: (MonadPlus m) => ... -> m String
    getString (String o) = return o
    getString _          = mzero
    
    getObject :: ...
    getObject (Object o) = return o
    getObject _          = mzero
    

    现在可以用Maybemonad中的计算替换代码段:

    do -- in the Maybe moned
      res3 <- getObject =<< LHashMap.lookup "base123" res2
      key1 <- getString =<< LHashMap.lookup "key1" res3
      key2 <- getString =<< LHashMap.lookup "key2" res3
      key3 <- getString =<< LHashMap.lookup "key2" res3
      return (key1, key2, key3)
    

    如果一切顺利,你会得到Just (...),否则Nothing(你可以变成一个IO错误).


    旁白:请注意,嵌套case表达式不会按预期工作,因为它们会形成不完整的模式.例如,如果您定义

    test x y =
      case x of
        Just x' ->
          case y of
            Just y' -> True
        _ -> False
    

    然后test (Just 0) Nothing失败了.该_模式仅适用于最外层case,而不适用于内部模式.

    此外,我建议从部分中拆分纯部分(在纯函数中处理数据)IO.

    2023-01-01 07:19 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有