Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do a recursive main function in Haskell

So I wanted to make a program that prints out a ASCII triangle. The program asks you the height that you want your triangle to be and prints it out and does it until the number you put in is 7, then it draws the traingle of height 7 and the program stops. The trinagle that is print out is made out of '*' if the number is even and '#' if the number is odd. I wanted to do that by recursively calling the main function

triangle level character = draw level character 1 
      where draw level character accumulator
             | level <=0 = putStr("")
             | level > 0 = do 
                 putStrLn (replicate level ' ' ++ replicate accumulator character)
                 draw (level-1) character (accumulator+2)

main = do
    number <- readLn :: IO Int
    if(number `mod` 2 == 0)
      then
        triangle number'*'
        main 
     else if (number == 7)
       then triangle number '#'
       else triangle number '#'
       main

The error i get is

• Couldn't match expected type ‘IO () -> IO ()’
              with actual type ‘IO ()’
• The function ‘triangle’ is applied to three arguments,
  but its type ‘Int -> Char -> IO ()’ has only two
  In the expression: triangle number '*' main
  In a stmt of a 'do' block:
    if (number `mod` 2 == 0) then
        triangle number '*' main
    else
        if (number == 7) then
            triangle number '#'
        else
            triangle number '#' main
like image 289
m3k_1 Avatar asked Feb 07 '26 00:02

m3k_1


2 Answers

The error says:

• The function ‘triangle’ is applied to three arguments,
  but its type ‘Int -> Char -> IO ()’ has only two
  In the expression: triangle number '*' main

So the compiler has parsed your code as triangle number '*' main; this means that the line break between triangle number '*' and main is not being interpreted as a do block statement break, and the reason is that you’re not inside a do block! The same error follows in the later else clause for triangle number '#' main. The fix is to add a do:

main = do
    number <- readLn :: IO Int
    if(number `mod` 2 == 0)
      then do
        triangle number'*'
        main 
     else if (number == 7)
       then triangle number '#'
       else do
         triangle number '#'
         main

If you have trouble with Haskell’s layout rules, you can always use explicit curly braces {} around blocks and semicolons ; between block items, to check your understanding of how your code is being parsed.

main :: IO ();  -- end signature
main = do {  -- begin ‘do’
    number <- readLn :: IO Int;  -- end statement
    if (number `mod` 2 == 0)
      then do {
        triangle number '*';
        main;
      }
      else if (number == 7)
      then triangle number '#'
      else do {
        triangle number '#';
        main;
      };  -- end ‘do’, end statement (starting with ‘if…’)
};  -- end ‘do’, end equation

Aside, note that I put spaces around the character literal like … number '*' … instead of … number'*' …. Apostrophes are allowed in identifiers (as an ASCII approximation of the “prime” symbol  ′  ), so if you changed this to number'X' then that would be parsed as a single name, number'X', rather than a name number followed by a character literal 'X', which may be confusing.

like image 184
Jon Purdy Avatar answered Feb 12 '26 05:02

Jon Purdy


First, you're missing a do here:

      then
        triangle number'*'
        main 

The two lines after then are being interpreted together as triangle number '*' main (which is how it's quoted in the error message if you notice). In order to have the two lines interpreted separately as subsequent actions, they need to be inside a do:

      then do
        triangle number'*'
        main 

And the same problem, plus ambiguous indentation, exists with the very last block. It should probably be:

else do
  triangle number '#'
  main
like image 41
Fyodor Soikin Avatar answered Feb 12 '26 04:02

Fyodor Soikin