Joes de Jonge
Joes de Jonge

Reputation: 181

How can you use the result of a function as a variable in another function in Haskell?

I'm trying to learn how to use Haskell and now I have to make a program that takes a integer n and a string k and every letter of that string will be moved n places to the right in the alphabet. At this moment I've got the next code:

import Data.Char

main = do
    x <- read getLine :: Int
    y <- getLine
    caesar x y

result :: String

rotate :: Int -> Char -> [Char]
rotate a b = [chr ((a + ord b) `mod` ord 'z' + ord 'a')]

caesar :: Int -> String -> ()
caesar moving text= do
    rotatespecific moving text 0
    putStrLn result


rotatespecific :: Int -> String -> Int -> ()
rotatespecific moving text place = do
    if place < length text
        then
            result ++ rotate (moving (text !! place))
            rotatespecific (moving text (place + 1))
        else
            if place == length text
                then
                    result ++ rotate (moving (text !! place))

But I can't compile it because it still gives me the same error message:

parse error (possibly incorrect indentation or mismatched brackets)
   |
28 |                     result ++ rotate (moving (text !! place))
   |                                                              ^

But I can't see what's wrong with my syntax. I first thought it had something to do with using a Char as parameter for my function but I was wrong because text !! place should give a char and not a [char]. So what's wrong with what I'm doing?

After some edit I got this, but it still doesn't work:

import Data.Char

main = do
    xr <- getLine
    let x = read xr :: Int
    y <- getLine
    putStrLn (rotatespecific (x y 0))

rotate :: Int -> Char -> [Char]
rotate a b = [chr ((a + ord b) `mod` ord 'z' + ord 'a')]

rotatespecific :: Int -> String -> Int -> String
rotatespecific moving text place = do
    if place < length text
        then do
            help <- text !! place
            h <- rotate (moving help)
            a <- rotatespecific (moving text (place + 1))
            b <- h ++ a
            return b

        else
            if place == length text
                then do
                    return rotate (moving (text !! place))
                else
                    return ()

Upvotes: 1

Views: 639

Answers (2)

luqui
luqui

Reputation: 60463

I feel like this is an appropriate time to just show an example, because there are a lot of little problems. I'm not going to fix logic bugs, but I've fixed your syntax. Hopefully this gets you unstuck.

rotatespecific :: Int -> String -> Int -> String
rotatespecific moving text place =
    if place < length text then
        -- use let .. in instead of do/bind (<-) in pure functions. 
        let help = text !! place

            -- multiple arguments are given after the function, no parentheses
            h = rotate moving help

            -- use parentheses around an argument if it is a complex expression
            -- (anything more than a variable name)
            a = rotatespecific moving text (place+1)

            b = h ++ a
        in b
    else
        if place == length text then
            rotate moving (text !! place)
        else
            undefined -- you must decide what String to return in this case.

After you have this function working as intended, and only then, open this sealed envelope. ♥️

 rotatespecific :: Int -> String -> String
 rotatespecific moving text = concatMap (rotate moving) text

Upvotes: 0

luqui
luqui

Reputation: 60463

The immediate problem is that every if must have an else. You got a parse error at the end because the parser is expecting more, namely an else for that if place == length text.

When you fix this you will have more problems, because you are treating Haskell like an imperative language, and that's not how she likes to be treated. It seems like you think

result ++ newstuff

will mutate result, adding newstuff to the end of it. But Haskell doesn't mutate. Instead, this expression result ++ newstuff is the list that results when you concatenate result and newstuff, but result itself remains unchanged.

ghci> let result = [1,2,3]
ghci> result ++ [4,5,6]
[1,2,3,4,5,6]
ghci> result
[1,2,3]

rotatespecific must return the rotated string, rather than trying to mutate it into existence. The only way functions may communicate is by returning results computed from their arguments -– they may not manipulate any "global" state like result. A function that returns () is guaranteed to be useless.

rotatespecific :: Int -> String -> Int -> String

Delete the result "global variable" (which does not mean what you think it means) and focus on defining rotatespecific in a way that it returns the rotated string.

I would also recommend commenting out main and caesar for now until you have rotatespecific compiling and working when you test it in ghci.

Upvotes: 4

Related Questions