caffeinated-fox
caffeinated-fox

Reputation: 115

errors when I try to return a list

Now I improved the code a bit, cut something off it, etc.
Here is the source code:

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

So now, when I try try to compile it, I become this error:

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

As I understand it, it trys to return a list of a list but it don't cause off:

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

But if I write

else return $ dungeon

instead, I get this error:

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

When I write it without $, I get this:

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

So how can I return it as type Dungeon?

Upvotes: 0

Views: 132

Answers (4)

caffeinated-fox
caffeinated-fox

Reputation: 115

I solved It.
As you can see it in the code
I have solved it by making:

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

Thanks too all of you! It helped a lot :D.

Upvotes: 0

Free_D
Free_D

Reputation: 577

Since mkDungeon has a type signature of mkDungeon :: Int -> Int -> Dungeon -> Dungeon, and Dungeon is the equivilent to [Tile], this function is in the list monad.

return is a function defined for monads which lifts a value into monad you are using. return for lists is defined as

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

So since dungeon has type Dungeon, when you pass it to return you get [dungeon]. This is not what you want and does not match the type signature which is why you get the error.

You do not need to use return at all in this case, removing it should solve your problem. Just remember the return in Haskell is nothing like return in imperative languages; it is not used leave functions.

Upvotes: 0

MathematicalOrchid
MathematicalOrchid

Reputation: 62848

So another answer already explained that you need to use let x = ... for normal values, and only use x <- ... for monadic actions. So that's one of your problems.


You also don't need all those do blocks in mkDungeon. Rather than

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

you want something like

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

In other words, pass the new dungeon to a recursive call to mkDungeon. But of course, this is the wrong way round: the new tile should be to the left of the (:) operator, not the right.

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

The next problem is that you have

data Dungeon = Dungeon [Tile]

That means that if x, y and z are Tile values, then

Dungeon [x, y, z]

is a Dungeon value, however

[x, y, z]

by itself is not. Your type signature for mkDungeon claims it takes a Dungeon and returns another Dungeon, but in fact it seems to be trying to take a list of tiles and return another list of tiles.

There are two ways to fix this. One is to make Dungeon a mere type alias rather than a whole new type:

type Dungeon = [Tile]

Now Dungeon and [Tile] are interchangeable, one merely being an alias to the other. Alternatively, you need to insert Dungeon all over the place:

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

Upvotes: 0

sepp2k
sepp2k

Reputation: 370415

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

If we remove the syntactic sugar from this, we get:

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

What the error message is complaining about is the let block contains a type signature for theDungeon, but no actual definition. The next problem would be that mkDungeon 0 0 [] produces a value of type Dungeon, which is not a monad, so you can't use >>= (and by extension <-) with it.

To define theDungeon properly you need to use = instead of <- (<- is for "extracting" values from monads and it's desugared using >>=, = is used for let (and global) bindings) and indent it in such a way that it's still part of the let block. So:

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

Or you can skip the type signature and just write let theDungeon = mkDungeon 0 0 [].

Upvotes: 2

Related Questions