2
votes

I am working on a function in Haskell that receives a string containing the width and height of a rectangle (this string is formatted like "width length" or "1 3"). It then recursively works out the side lengths of squares that would fill in that rectangle and returns all of the side lengths of every square filling the rectangle up (it would only return one side length per square because squares only have one unique side length).

The problem I have run into is that whenever I am running the below code Haskell tells me that I am trying to return type [[Int]] when type [Int] is expected. I am quite new to Haskell and struggling to figure out what exactly is causing my Int to be turned into a [Int] before it is put into a set and returned.

module MakeSquares where

import Data.List

solve :: String -> [Int]

solve s
 |lengthOne == lengthTwo = return [lengthOne]
--If we run into any problems then add a where statement definining the equation in "show"
 |lengthOne > lengthTwo = do f <- solve (intercalate " " [show lengthOneMinusTwo, show lengthTwo])
                             return (lengthTwo:f)
 |lengthOne < lengthTwo = do f <- solve (intercalate " " [show lengthTwoMinusOne, show lengthOne])
                             return (lengthOne:f)
 where
  input = words s
  stringLengthOne = input!!0
  stringLengthTwo = input!!1
  lengthOne = read stringLengthOne::Int
  lengthTwo = read stringLengthTwo::Int
  lengthOneMinusTwo = lengthOne - lengthTwo
  lengthTwoMinusOne = lengthTwo - lengthOne

The error that Haskell sends me when attempting to run this is below.

MakeSquares.hs:8:28: error:
    * Couldn't match type `[Int]' with `Int'
      Expected type: [Int]
        Actual type: [[Int]]
    * In the expression: return [lengthOne]
      In an equation for `solve':
          solve s
            | lengthOne == lengthTwo = return [lengthOne]
            | lengthOne > lengthTwo
            = do f <- solve (intercalate " " [show lengthOneMinusTwo, ....])
                 return (lengthTwo : f)
            | lengthOne < lengthTwo
            = do f <- solve (intercalate " " [show lengthTwoMinusOne, ....])
                 return (lengthOne : f)
            where
                input = words s
                stringLengthOne = input !! 0
                stringLengthTwo = input !! 1
                lengthOne = read stringLengthOne :: Int
                ....
  |
8 |  |lengthOne == lengthTwo = return [lengthOne]
  |                            ^^^^^^^^^^^^^^^^^^

MakeSquares.hs:11:30: error:
    * Couldn't match type `[Int]' with `Int'
      Expected type: [Int]
        Actual type: [[Int]]
    * In a stmt of a 'do' block: return (lengthTwo : f)
      In the expression:
        do f <- solve
                  (intercalate " " [show lengthOneMinusTwo, show lengthTwo])
           return (lengthTwo : f)
      In an equation for `solve':
          solve s
            | lengthOne == lengthTwo = return [lengthOne]
            | lengthOne > lengthTwo
            = do f <- solve (intercalate " " [show lengthOneMinusTwo, ....])
                 return (lengthTwo : f)
            | lengthOne < lengthTwo
            = do f <- solve (intercalate " " [show lengthTwoMinusOne, ....])
                 return (lengthOne : f)
            where
                input = words s
                stringLengthOne = input !! 0
                stringLengthTwo = input !! 1
                lengthOne = read stringLengthOne :: Int
                ....
   |
11 |                              return (lengthTwo:f)
   |                              ^^^^^^^^^^^^^^^^^^^^

MakeSquares.hs:11:48: error:
    * Couldn't match expected type `[Int]' with actual type `Int'
    * In the second argument of `(:)', namely `f'
      In the first argument of `return', namely `(lengthTwo : f)'
      In a stmt of a 'do' block: return (lengthTwo : f)
   |
11 |                              return (lengthTwo:f)
   |                                                ^

MakeSquares.hs:13:30: error:
    * Couldn't match type `[Int]' with `Int'
      Expected type: [Int]
        Actual type: [[Int]]
    * In a stmt of a 'do' block: return (lengthOne : f)
      In the expression:
        do f <- solve
                  (intercalate " " [show lengthTwoMinusOne, show lengthOne])
           return (lengthOne : f)
      In an equation for `solve':
          solve s
            | lengthOne == lengthTwo = return [lengthOne]
            | lengthOne > lengthTwo
            = do f <- solve (intercalate " " [show lengthOneMinusTwo, ....])
                 return (lengthTwo : f)
            | lengthOne < lengthTwo
            = do f <- solve (intercalate " " [show lengthTwoMinusOne, ....])
                 return (lengthOne : f)
            where
                input = words s
                stringLengthOne = input !! 0
                stringLengthTwo = input !! 1
                lengthOne = read stringLengthOne :: Int
                ....
   |
13 |                              return (lengthOne:f)
   |                              ^^^^^^^^^^^^^^^^^^^^

MakeSquares.hs:13:48: error:
    * Couldn't match expected type `[Int]' with actual type `Int'
    * In the second argument of `(:)', namely `f'
      In the first argument of `return', namely `(lengthOne : f)'
      In a stmt of a 'do' block: return (lengthOne : f)
   |
13 |                              return (lengthOne:f)
   |                                                ^

The way I have been attempting to debug this program is by manually sending commands in the order I think the program would send them in ghci. Unfortunately I have not been able to find what is causing my typing to be incorrect yet.

2
You really shouldn't use !! or strings as input. In haskell, you should youse datatypes to represent complex data structuresAuscyber

2 Answers

4
votes

return is not, as in procedural languages a way to express that this is the result of the function. return wraps items in a monadic context. Since here the return type of your function is [Int], return uses the [] instance of Monad, and return thus wraps the item in a singleton list.

You should use:

solve :: String -> [Int]
solve s
 | lengthOne == lengthTwo = [lengthOne]  -- &leftarrow; no return
 | lengthOne > lengthTwo = do
    f <- solve (intercalate " " [show lengthOneMinusTwo, show lengthTwo])
    (lengthTwo:f)  -- &leftarrow; no return
 |lengthOne < lengthTwo = do
    f <- solve (intercalate " " [show lengthTwoMinusOne, show lengthOne])
    (lengthOne:f)  -- &leftarrow; no return

But now we will have another problem: f has type Int, but then this does not work with (lengthOne:f), since that expects fto be a list. You do not need to usedo` here, and simply work with a subexpression, or a let … in …:

solve s
 | lengthOne == lengthTwo = [lengthOne]
 | lengthOne > lengthTwo =
    --            &downarrow; subexpression
    (lengthTwo: solve (intercalate " " [show lengthOneMinusTwo, show lengthTwo]))  
 |lengthOne < lengthTwo =
    --            &downarrow; subexpression
    (lengthOne:solve (intercalate " " [show lengthTwoMinusOne, show lengthOne]))
3
votes

The problem is your do blocks. Don't use do or return for anything but IO sequencing until you properly understand about monads.

What you meant to write was

 |lengthOne == lengthTwo = [lengthOne]
 |lengthOne > lengthTwo = let f = solve (intercalate " " [ show lengthOneMinusTwo
                                                         , show lengthTwo ])
                          in (lengthTwo:f)
 |lengthOne < lengthTwo = let f = solve (intercalate " " [ show lengthTwoMinusOne
                                                         , show lengthOne ])
                          in (lengthOne:f)

Or, using pattern guards,

 | lengthOne == lengthTwo = [lengthOne]
 | lengthOne > lengthTwo
 , f <- solve (intercalate " " [show lengthOneMinusTwo, show lengthTwo])
         = lengthTwo:f
 | lengthOne < lengthTwo
 , f <- solve (intercalate " " [show lengthTwoMinusOne, show lengthOne])
         = lengthOne:f

Honestly, passing strings to convey a list of numbers is pretty horrible style. It seems like this function should actually have type

solve :: Int -> Int -> [Int]

and then you can ditch all the words, read, show and intercalate crud.