当我尝试 return 列表时出错

errors when I try to return a list

现在我改进了代码,删掉了一些东西,等等
这是源代码:

import Prelude


{-- DEFINE THE TYPES --}
data Tile = Tile  -- the tiles of the map
    {char :: Char
    ,isBlocking :: Bool
    ,position :: (Int,Int)}

type Dungeon = [Tile]  -- the dungeon


{-- THE MAIN FUNCTION --}
main :: IO ()
main = do
    let theDungeon :: Dungeon
        theDungeon = mkDungeon 0 0 []
    writeFile "./test.log" $ show theDungeon


{-- DEFINE THE SIZE OF THE MAP --}
screenX = 80
screenY = 24

mapX = screenX
mapY = screenY - 4

{-- THE FUNCTIONS FOR THE DUNGEON --}
mkDungeon :: Int -> Int -> Dungeon -> Dungeon -- function for generating the dungeon
mkDungeon x y dungeon =
    if x > mapX  -- looks if the line isn't too long
        then mkDungeon 0 (y + 1) dungeon  -- it's too long, so make the next line
        else if y == 0  -- if it at the top
            then mkDungeon (x + 1) y $ dungeon ++ [Tile '#' True (x, y)}
            else if y > 0 && y < mapY  -- looks if the line is in the middle
                then if x == 0 || x == mapX  -- is it at the right or at the left
                    then mkDungeon (x + 1) y $ dungeon ++ [Tile '#' True (x, y)]
                    else mkDungeon (x + 1) y $ dungeon ++ Tile '.' False (x, y)]
                else if y == mapX  -- looks if it is at the bottom
                    then do mkDungeon (x + 1) y  $ dungeon ++ [Tile '#' True (x, y)]
                    else return $ dungeon :: Dungeon

所以现在,当我尝试编译它时,出现了这个错误:

main.hs:42:26:
    Couldn't match type ‘[Tile]’ with ‘Tile’
    Expected type: Dungeon
      Actual type: [Dungeon]
    In the expression: return $ dungeon :: Dungeon
    In the expression:
    ...

据我了解,它会尝试 return 一个列表的列表,但不会导致关闭:

mkDungeon :: Int -> Int -> Dungeon -> Dungeon

但是如果我写

else return $ dungeon

相反,我收到此错误:

main.hs:42:26:
    Couldn't match type ‘[Tile]’ with ‘Tile’
    Expected type: Dungeon
      Actual type: [Dungeon]
    In the expression: return $ dungeon
    ...

当我不写 $ 时,我得到这个:

main.hs:42:26:
    Couldn't match type ‘[Tile]’ with ‘Tile’
    Expected type: Tile
      Actual type: Dungeon
    In the expression: return dungeon
    ...

那么我怎样才能return它是地牢类型呢?

main = do
    let theDungeon :: Dungeon 
    theDungeon <- mkDungeon 0 0 []
    writeFile "./test.log" $ show theDungeon

如果我们从中删除语法糖,我们得到:

main =
    let
        theDungeon :: Dungeon
    in
        mkDungeon 0 0 [] >>= \theDungeon ->
        writeFile "./test.log" $ show theDungeon

错误消息抱怨的是 let 块包含 theDungeon 的类型签名,但没有实际定义。下一个问题是 mkDungeon 0 0 [] 产生一个 Dungeon 类型的值,它不是 monad,所以你不能使用 >>= (以及扩展 <-)它。

要正确定义 theDungeon,您需要使用 = 而不是 <-<- 用于来自 monad 的 "extracting" 值,它使用 >>== 用于 let(和全局)绑定)并缩进它,使其仍然是 let 块的一部分。所以:

main = do
    let theDungeon :: Dungeon 
        theDungeon = mkDungeon 0 0 []
    writeFile "./test.log" $ show theDungeon

或者你可以跳过类型签名,只写 let theDungeon = mkDungeon 0 0 [].

所以另一个答案已经解释了你需要使用 let x = ... 来表示正常值,并且只使用 x <- ... 来表示单子动作。所以这是你的问题之一。


您也不需要 mkDungeon 中的所有那些 do 块。而不是

then do 
  dungeon : Tile '#' True (x,y)
  mkDungeon (x + 1) y

你想要像

这样的东西
then mkDungeon (x + 1) y (dungeon : Tile '#' True (x,y))

换句话说,将新地牢传递给对mkDungeon的递归调用。但是,当然,这是错误的方法:新图块应该位于 (:) 运算符的 左侧 ,而不是右侧。

then mkDungeon (x + 1) y (Tile '#' True (x,y) : dungeon)

下一个问题是你有

data Dungeon = Dungeon [Tile]

这意味着如果xyzTile值,那么

Dungeon [x, y, z]

是一个Dungeon值,但是

[x, y, z]

本身不是。 mkDungeon 的类型签名声称它需要一个 Dungeon 和 return 另一个 Dungeon,但实际上它似乎试图获取一个图块列表和 return 另一个瓷砖列表。

有两种方法可以解决这个问题。一种是使 Dungeon 成为一个类型别名而不是一个全新的类型:

type Dungeon = [Tile]

现在 Dungeon[Tile] 可以互换,一个只是另一个的别名。或者,您需要在整个地方插入 Dungeon

mkDungeon x y (Dungeon ts) = ...
   ...then mkDungeon (x + y) y (Dungeon (Tile '#' True (x,y) : ts))
   ...

因为 mkDungeon 的类型签名是 mkDungeon :: Int -> Int -> Dungeon -> Dungeon,并且 Dungeon 等价于 [Tile],所以这个函数在列表 monad 中。

return 是为 monad 定义的函数,它将一个值提升到您正在使用的 monad 中。 return 对于列表定义为

return :: a -> [a]
return x = [x]

因此,由于 dungeon 的类型为 Dungeon,当您将其传递给 return 时,您会得到 [dungeon]。这不是您想要的,并且与类型签名不匹配,这就是您收到错误的原因。

在这种情况下您根本不需要使用 return,删除它应该可以解决您的问题。请记住 Haskell 中的 return 与命令式语言中的 return 完全不同;它没有使用休假功能。

我解决了。
正如您在 code
中看到的那样 我已经通过制作解决了它:

... $ dungeon ++ [Tile ...]

也谢谢大家!帮了大忙 :D.