vucko95
vucko95

Reputation: 103

haskell - Couldn't match type ‘(Char, Int)’ with ‘[Char]’ error

I am writing a function to expand a String

Example:

foo "a4b4"

should give back:

"aaaabbbb"

Here is my code:

foo :: String -> String
foo (x:xs) = let curent = fooHelp(x, read $ charToString( xs !! 0  ) :: Int)
             in x : (curent) ++ foo (tail xs)

fooHelp:

fooHelp :: String -> Int -> String
fooHelp x n 
             | n >= 3 = x ++ fooHelp x (n - 1) 
             | n == 2 = x
             | n == 1 = ""

and charToString:

charToString :: Char -> String
charToString c = [c]

it takes x and appends it to the current. In current fooHelp will give back expanded String

example : foo "a4b4" then x = "a", xs = "4b4", xs !! 0 = '4' read $ charToString( xs !! 0 ) :: Int) will convert char '4' to int 4 and pass it to fooHelp alongside with x("a") -> fooHelp(x, 4) and give back "aaa". Then x : current should give back "aaaa"because x = "a" and current "aaa" and then recursive call with ++ foo tail xs where xs ="b4" and it should repeat the process.

I am getting the error:

test.hs:173:34: error:
    • Couldn't match type ‘(Char, Int)’ with ‘[Char]’
      Expected type: String
        Actual type: (Char, Int)
    • In the first argument of ‘fooHelp’, namely
        ‘(x, read $ charToString (xs !! 0) :: Int)’
      In the expression:
        fooHelp (x, read $ charToString (xs !! 0) :: Int)
      In an equation for ‘curent’:
          curent = fooHelp (x, read $ charToString (xs !! 0) :: Int)
    |
173 | foo (x:xs) = let curent = fooHelp(x, read $ charToString( xs !! 0  ) :: Int)
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Where did I make mistake? I tested function fooHelp and it works fine with such arguments as in foo.

Testing fooHelp:

xs = "4b4"

test =  read $ charToString( xs !!0 ) :: Int

*Main> test
4

Upvotes: 1

Views: 716

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477190

A classic mistake is to call a function like f (x1, x2). In Haskell functions have one parameter, and often this is not a tuple.

Your fooHelper function has type:

fooHelp :: String -> (Int -> String)

so it is a function that takes a String, and the returns a function that maps Ints on Strings. You thus should call the function like:

(fooHelp x) (read $ charToString( xs !! 0  ) :: Int)

or less verbose:

(fooHelp x) (read $ charToString( xs !! 0  ) :: Int)

But now the types still not match: x is a Char, not a String, you can wrap it in a list, like:

fooHelp [x] (read $ charToString( xs !! 0  ) :: Int)

like:

foo :: String -> String
foo (x:xs) = let curent = fooHelp [x] (read $ charToString( xs !! 0  ) :: Int)
             in x : (curent) ++ foo (tail xs)

But still this function has a problem: we iterate over the string, so eventually we will reach an empty string, and foo has no case for that. We need to return the empty string in that case, like:

foo :: String -> String
foo (x:xs) = let curent = fooHelp [x] (read $ charToString( xs !! 0  ) :: Int)
             in x : (curent) ++ foo (tail xs)
foo [] = ""

But it is still not very elegant. We here perform a lot of unnecessary operations, like wrapping characters to strings, etc.

We can make use of the replicate :: Int -> a -> [a] function to repeat a character a given number of times. For example:

Prelude> replicate 3 'a'
"aaa" 

Furthermore the digitToInt :: Char -> Int function can parse a digit character to the corresponding Int:

Prelude Data.Char> digitToInt '3'
3 

So we here can use these two to each time take the first two characters of the string, and use replicate (digitToInt k) x to generate a string where x si repeated the requested amount of time, and perform recursion on the rest of the string, like:

import Data.Char(digitToInt)

foo :: String -> String
foo (x:k:xs) = replicate (digitToInt k) x ++ foo xs
foo _ = ""

Upvotes: 4

jwodder
jwodder

Reputation: 57590

The expression fooHelp(x, read $ charToString( xs !! 0 ) :: Int) tries to pass a single argument — a (Char, Int) pair — to fooHelp, which is wrong. Presumably, you want to instead write:

fooHelp (charToString x) (read $ charToString( xs !! 0  ) :: Int)

Upvotes: 4

Related Questions