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

时间:2016-04-02 03:58:17

标签: haskell pattern-matching parameter-passing compiler-warnings

这是我的功能。

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 个答案:

答案 0 :(得分: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类型。这完全消除了基于长度的模式匹配问题,并使您的功能更容易从外部世界理解。&#34;

在不了解更多背景的情况下,我无法确切地说明在您的情况下哪种常数大小的数据结构最合适,但这里有一个简单的例子,希望能够说明这种方法的好处。&#39 ; ve描述:

--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.

在编译时遇到GHC无法保证的要求(即,正好传递六个值)这一事实将无法解决这个问题。即使您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 :(得分:1)

我感谢我从这次经历中获得的投入和学习。但是,我已经从一个发出编译时警告的工作函数中取得了进展,我忽略了一个编译错误,导致无法构建输出可执行文件。

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

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

感谢所有的投入和指导。

戴夫史密斯