1

I'm trying to add certain letters of a String in a String list.

For example: "Haskell" -> ['a', 's'] (only a and s)

After run I get the error message "Note in scope: xs". So it lacks a list(I think). I have no problems to convert a list to a new list (e.g. mirrorList), but how can I "create" a list after I get a String Input?

letterList :: String -> [String]
letterList s = let x = head s in 
                     if x == 'a' || x == 's' then x:xs 
                     else letterList (tail x)
2
  • What is mirrorList? Commented Jan 25, 2016 at 22:32
  • mirror :: [a] -> [a] mirror [] = [] mirror (x:xs) = [x] ++ mirror xs ++ [x] Commented Jan 25, 2016 at 22:58

4 Answers 4

6

Before getting to your precise question, let's fix the types. Your example "Haskell" -> ['a', 's'] indicates that the type of this function is String -> String, not String -> [String]. Note that String is just a type alias for [Char].

The error message means you have an expression that refers to a value xs, but nowhere have you defined anything named xs. I suspect you were going for with x:xs is pattern matching. The pattern goes to the left of the =, and that declares x and xs which you can refer to on the right side. And since the empty list can't be destructured into a head and tail, you need a separate case for it.

I'm guessing the result you're after looks something like this:

letterList :: String -> String
letterList []     = []
letterList (x:xs) = if x == 'a' || x == 's' then x:rest else rest
                    where rest = letterList xs

I assume your use of recursion here is just for practice, but for what it's worth, I would recommend writing this as

letterList :: String -> String
letterList = filter (`elem` "as")
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, Chris! You helped me a lot especially with this part: where rest = (letterList xs) . I also tried String -> [Char] and it also works. letterList x = let s = head x in if s == 'a' || s == 's' then s:rest else letterList rest where rest = (letterList (tail x))
2
  • Not in scope: xs

    That just means what it says: there is no variable called xs to be found, yet you attempt to use it. It's much the same error you'd also get in Java when trying to, say, call a method that's nowhere defined.

  • I have no problems to convert a list to a new list (e.g. mirrorList)

    That's something you never need in Haskell. When you have a list you can always use it directly and in whichever way you fancy. Because of referential transparency, there can never be a difference between a value and a copy of it. And I'm not sure what you mean by “converting” here, but generally it is avoided to convert anything in Haskell: parametric polymorphism usually allows you to generate the right type right from the beginning.

Now as to “how do I create a list” – you already do it! The cons (prepend) operator : in x:xs constructs a new list. ...Granted, it's based on the already existing list xs. This is in fact the only way to generate a new list, because the list constructor always needs a tail to prepend elements before. If you don't want/need/have any such list, use the empty one, [], that's always available.


Even in Haskell, you sometimes need to work with mutable data (usually arrays in the ST monad – mutable internally but still guaranteed referentially transpart when viewed from the outside). Within such a computation, it may be necessary to copy an array, just like it can be in procedural languages.

2 Comments

Thank you for the answer. I did not express myself well. My mistake. I don't convert a list to a new list, I just mirror the same list. mirror :: [a] -> [a] mirror [] = [] mirror (x:xs) = [x] ++ mirror xs ++ [x]
Aha. Note that you could simply define that as mirror xs = xs ++ reverse xs.
1

The error message says - you are using xs on the right hand side but do not define it before its usage (i.e. no let statement before, no where statement after, and not on the left hand side either.

Aside from this answer

You have another problem is that you are basically implementing a filter, but the result type is a String not a [String]!

letterList :: String -> [String]
letterList [] = []
letterList (x:xs) = if x == 'a' || x == 's'
                      then [x]: letterList xs 
                      else letterList xs
  • The first case prevents an error if you supply an empty String!
  • the next line deconstructs the String in head = x and tail = xs, which is more readable and correct (tail x is wrong you try to take the end of a single letter!)
  • next [x] transforms a single letter Char in a String = [Char].

More elegant/homework

letterList str = [[x] | x <- str, x `elem` "as"]

this is called list comprehension, which would be the next "homework" I will give you to think about.

or

letterList str = map (\x -> [x]) $ filter (`elem` "as") str

Which uses higher order functions (filter and map) which is the second task I'll give you.

Edit

Of course you can also change the type signature to String -> String as @ChrisMartin shows - then you can omit the [,] in the [x] statements, and respectively omit the map (\x -> [x]) thing.

Comments

-2

Seems to be a copy-paste error: try to replace the xs with (tail x) ... and then think about the emoty-list problem ... Maybe you will then replace the left Hand side too ;-)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.