【问题标题】:Haskell -- Unexpected expected typeHaskell——意外的预期类型
【发布时间】:2014-04-29 14:27:44
【问题描述】:

我正在尝试在 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] 吗?

提前感谢您的帮助。

【问题讨论】:

  • do 表示法意味着您的返回类型是一个 monad,例如 [a]IO aMaybe a 等。元组本身不构成一个 monad,所以您不能将do 表示法或return 与它们一起使用。
  • 很好的问题标题!

标签: haskell recursion types infinite


【解决方案1】:

return 接受t 类型的值并产生m t 类型的值,其中m 是一些单子。您的函数的结果是将return 应用于参数的结果。那么如果结果是([a], String) 类型,那么该参数必须具有哪种类型?好吧,return x 可以产生([a], String) 类型值的唯一方法是,如果x 具有[String] 类型并且m 是类型构造函数(,) [a](当时不考虑,前奏曲中大概不存在这样的例子)。因此类型检查器期望参数是[String]

【讨论】:

    【解决方案2】:

    在这种情况下,您不能使用 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
    

    【讨论】:

      【解决方案3】:

      do 用于单子函数,但您的函数是纯函数:

      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)。

      【讨论】:

      • 虽然这是真的(尽管 GHC 实际上并不关心),但这里的问题实际上是 return - 比 do 更严重。
      • @sepp2k 好点,原来的&gt;&gt;= 块中并没有真正的do
      猜你喜欢
      • 2014-09-10
      • 1970-01-01
      • 1970-01-01
      • 2011-11-30
      • 1970-01-01
      • 2023-02-12
      • 2018-05-16
      • 2021-05-28
      • 1970-01-01
      相关资源
      最近更新 更多