首页 文章

Haskell - 出乎意料的预期类型

提问于
浏览
3

我正在尝试在Haskell中编写一个函数,它会弹出一些已经序列化为字符串列表的项目 . (此列表表示文本文件的行 - 一行不一定是一个项目)

使用另一个函数作为参数调用该函数 . 此函数将关闭单个项目,并返回包含该项目和文本其余部分的元组 . 该函数应该递归执行此操作n次,每次添加结果列表 . 它返回此列表以及文本的其余部分,以用于进一步解析 .

popN :: Integer -> [String] -> ([String]-> (a, [String])) -> ([a], [String])
popN n txt fun  | n == 0 = ([], txt)
                | n /= 0 = do
                    let (tp, newtxt) = fun txt
                    let newnum = n - 1
                    let (rest, after) = popN newnum newtxt fun
                    return (tp : rest, after)

当我尝试编译此代码时,我收到以下错误:

Couldn't match the expected type '[String]' with actual type '([a], [String])'
In the first argument  of 'return', namely '(tp : rest, after)'

实际类型 ([a], [String]) 是我期望的类型 . 然而,我不明白为什么 [String] 是预期的类型 . 有人可以向我解释为什么GHC希望这个函数返回一个 [String]

在此先感谢您的帮助 .

3 回答

  • 5

    return 采用 t 类型的值并生成 m t 类型的值,其中 m 是某个monad . 函数的结果是将 return 应用于参数的结果 . 那么如果结果是 ([a], String) 类型,那么该参数必须具有哪种类型?好吧, return x 可以产生 ([a], String) 类型值的唯一方法是 x 类型为 [String]m 是类型构造函数 (,) [a] (没有考虑到,此时可能没有这样的实例存在于前奏中) . 因此,类型检查器期望参数为 [String] .

  • 0

    在这种情况下,您不能使用monad,因为您返回的元组不是monad . 你可以像这样使用简单的递归:

    popN :: Integer -> [String] -> ([String]-> (a, [String])) -> ([a], [String])
    popN 0 txt _    = ([], txt)
    popN n []  _    = ([], [])
    popN n txt fun  = (tp:rest, newtxt1)
        where
            (tp,   newtxt ) = fun txt
            (rest, newtxt1) = popN (n-1) newtxt fun
    
  • 0

    do 用于monadic函数,但是你的纯函数是:

    popN :: Integer -> [String] -> ([String]-> (a, [String])) -> ([a], [String])
    popN 0 txt _ = ([], txt)
    popN n txt fun = (a:as, txt'') where
        (a, txt') = fun txt
        (as, txt'') = popN (n-1) txt' fun
    

    似乎 txt 正在充当穿过该函数的状态,并且可以使用 State monad隐藏 .

    type S = State [String]
    
    popN :: Integer -> S a -> S [a]
    popN 0 _ = return []
    popN n m = do
        a <- m
        as <- popN (n-1) m
        return (a:as)
    

    但这基本上是 replicateM (如果你可以处理 Int 而不是 Integer ) .

相关问题