我正在尝试在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]
?
提前感谢您的帮助。
答案 0 :(得分:5)
return
采用t
类型的值,并生成m t
类型的值,其中m
是一些monad。函数的结果是将return
应用于参数的结果。那么如果结果是([a], String)
类型,那么该参数必须具有哪种类型?好吧,return x
生成([a], String)
类型值的唯一方法是x
类型为[String]
而m
是类型构造函数{{1 (在这一点上,没有考虑到,可能在前奏中没有这样的实例)。因此,类型检查器期望参数为(,) [a]
。
答案 1 :(得分: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
)。
答案 2 :(得分: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