Home Articles

Haskell函数中的非穷举模式匹配

Asked
Viewed 871 times
1

这是我的功能 .

toResult :: [SqlValue] -> IO Course
toResult [ fromSql -> courseid,   fromSql -> title,
       fromSql -> name,  fromSql -> version,
       fromSql -> cdate, fromSql -> fid ] = do
    let courseXML = show (fid :: Int)
    xml <- B.readFile courseXML
    let Right doc = parseXML courseXML xml
    let passing = read $ T.unpack $ fromJust
            $ lookup (T.pack "PassingScore")
            $ elementAttrs $ head $ docContent doc
    return (Course courseid title name version cdate passing)

对此函数的任何调用都不会传递一个只有六个SqlValue值列表的参数,这是一个错误 .

编译它会返回非穷举模式匹配警告:

Pattern match(es) are non-exhaustive
In an equation for `toResult':
    Patterns not matched:
        []
        [_]
        [_, _]
        [_, _, _]
        ...

我知道我可以忽略警告,但我正在努力学习如何正确地处理这个Haskell .

任何和所有建议都表示赞赏 .

戴夫

更新中...

根据此处提供的建议,我已将代码更改为当前形式 .

toResult :: (SqlValue, SqlValue, SqlValue, SqlValue, SqlValue, SqlValue) -> IO Course
toResult ( fromSql -> courseid,   fromSql -> title,
           fromSql -> name,  fromSql -> version,
           fromSql -> cdate, fromSql -> fid ) = do
    let courseXML = show (fid :: Int)
    xml <- B.readFile courseXML
    let Right doc = parseXML courseXML xml
    let passing = read $ T.unpack $ fromJust
                $ lookup (T.pack "PassingScore")
                $ elementAttrs $ head $ docContent doc
    return (Course courseid title name version cdate passing)

listToTuple ::  [SqlValue] -> (SqlValue, SqlValue, SqlValue, SqlValue, SqlValue, SqlValue)
listToTuple [a,b,c,d,e,f] = (a,b,c,d,e,f)
listToTuple xs            = error "Wrong number of values"

getCourses :: Connection -> IO [Course]
getCourses conn = do
    let query = "{Actual query deleted to protect the innocent.}"
    res <- quickQuery conn query []
    mapM toResult (listToTuple res)

但是,这将无法编译,并出现以下错误 . 这次我错过了什么简单的事情?

src\CDCQuarterly.hs:122:20:
    Couldn't match type `(,,,,,)
                           SqlValue SqlValue SqlValue SqlValue SqlValue'
                   with `[]'
    Expected type: [(SqlValue,
                     SqlValue,
                     SqlValue,
                     SqlValue,
                     SqlValue,
                     SqlValue)]
      Actual type: (SqlValue,
                    SqlValue,
                    SqlValue,
                    SqlValue,
                    SqlValue,
                    SqlValue)
    In the second argument of `mapM', namely `(listToTuple res)'
    In a stmt of a 'do' block: mapM toResult (listToTuple res)

src\CDCQuarterly.hs:122:32:
    Couldn't match type `[SqlValue]' with `SqlValue'
    Expected type: [SqlValue]
      Actual type: [[SqlValue]]
    In the first argument of `listToTuple', namely `res'
    In the second argument of `mapM', namely `(listToTuple res)'

2 Answers

  • 2

    我可以想出两种避免警告的方法 . 第一种也是最简单的方法是为 toResult 添加另一个方程式,该方程式捕获所有其他大小的列表并提供信息性错误:

    toResult :: [SqlValue] -> IO Course
    toResult [ ... ] = do ... --Your existing code goes here
    toResult xs      = error $ "toResult: List must contain exactly six values (recieved " ++ length xs ++ ")."
    

    但是如果你的函数只能在长度为6的列表上运行,我怀疑列表是否真的是这里使用的最佳数据结构 .

    理想情况下,您的函数类型签名应该让读者很清楚地知道如何使用该函数 . 如果您的函数只能接受长度为6的列表,那么它的类型签名无法通信 . 我建议选择不具有可变长度的不同数据结构,例如6元组(可能与 typenewtype 结合使用)或自定义 data 类型 . 这完全消除了基于长度的模式匹配问题,并使您的功能更容易理解"outside world."

    在不了解更多上下文的情况下,我无法确切地知道在您的情况下哪种常量大小的数据结构最合适,但这里有一个简单的例子,希望能够说明我所描述的方法的好处:

    --Explicitly hexadic type signature (might benefit from some aliasing)
    toResult :: (SQLValue, SQLValue, SQLValue, SQLValue, SQLValue, SQLValue) -> IO Course
    toResult ( fromSql -> courseid, fromSql -> title,
               fromSql -> name,     fromSql -> version,
               fromSql -> cdate,    fromSql -> fid ) = do ...
    --No need for separate equations due to constant-size container
    

    不幸的是,如果你无法从这种情况中完全删除 List 类型,那么所有这一切都会将问题转移到其他地方,因为你最终需要第二个函数来从List转换为元组 . These types of functions are invariably ugly and a pain to work with.

    它_10043261_重新100%, quickQuery 将返回一个正好六个元素的列表(你真的,完全,在数学上确定这个吗?),你仍然必须考虑到这种情况的逻辑可能性 . 这意味着您必须在运行时提供一种处理此案例的方法 .

    那么,当传递多于或少于六个值时,您想要做什么?如何在您的程序中实现错误处理?这是一个使用 error 的潜在天真的例子:

    sixValues :: [SQLValue] -> 
                 (SQLValue, SQLValue, SQLValue, SQLValue, SQLValue, SQLValue)
    sixValues [a,b,c,d,e,f] = (a,b,c,d,e,f)
    sixValues xs            = error "Wrong number of values"
    

    虽然没有比我的第一个建议好多少,但这至少具有将长度处理逻辑与结果生成逻辑分开的优点 .

  • 1

    我很欣赏从这次经历中得到的投入和学习 . 但是,我从一个发出编译时警告的工作函数发展而来,我很乐意忽略一个编译错误,阻止构建输出可执行文件 .

    我已经从这个主题中加入了一些建议,但基本上已经回到了我关于非详尽模式匹配的原始警告 . 最终,似乎在执行数据库查询之间存在不可调和的不一致性,该数据库查询总是返回相同数量的元素的列表,然后将该列表用作固定大小的集合,或者将该列表转换为固定大小的集合 .

    每种语言都有它的怪癖 . 也许这是Haskell的一个 .

    感谢所有的输入和指导 .

    戴夫史密斯

Related