3

I'm supposed to split a string and return the substring that occurs before a passed character, but we're just starting Haskell, and it is like Chinese to me. I've been messing with it, but no luck. Here's what I have so far:

--spanString returns substring of string s before char c
spanString (c, [s])::(c, [s]) -> []
spanString (c, a:b) =
   let (x, y) = spanString (c, b)
   in
      if a < c then (a:x,y)
      else (x, a:y)

What am I messing up?

4 Answers 4

4

First of all, your type signature is completely messed up. It must either be absent or be of the form spanString :: <some type>. Even if we ignore the (c, [s]) standing before the double colon, the rest is still something strange. One can read it as "a function taking values of type (c, [s]) to values of type [] for any c and s" (c and s are type variables). First, there is no type [] in Haskell. There is not going be a list type without its element type. Next, we can't work with any c and s. We must be able to compare them, right?

Actually, let's avoid using polymorphism for now and specify exactly which types we want. We want a character and a list of characters, packed up into a tuple for some reason: (Char, [Char]). Note that Char starts with a capital letter, which means it's not a type variable, but rather a concrete type. What about our result type? If you trust the problem description, you need to return a list of characters ([Char]), but if you look at the code, it obviously returns tuples of lists (([Char], [Char])). Okay, maybe the second list is useful, let's leave it for now:

spanString :: (Char, [Char]) -> ([Char], [Char])`

Now your code compiles.

However, when run, it crashes with exception: Non-exhaustive patterns in function spanString. This is because you don't handle the case when the passed list is empty. If you do that, by adding an equation like

spanString (_, []) = ([], [])

, your function runs well, but now let's look at what it does. It turns out you have a function for list partitioning: it returns all characters of the given string less than c as the first element of the tuple and all other characters as the second element. Seems like a bug to me (you've implemented a completely different function!).

Sign up to request clarification or add additional context in comments.

1 Comment

Yeah, the second returned list was my fault. I was kind of losing hope and willing to try anything when I first wrote this, but there's definitely only supposed to be one list returned. Seems like I'm going about this in the entirely wrong way, and don't know where to go from here.
3

Err, quite a lot.

First, your type declaration is wrong. Haskell uses upper case names for types, and it doesn't pass parameters in brackets like most languages do. We write

y = sin x

instead of

y = sin (x)

You probably want something like

spanString :: Char -> String -> String

Your definition of spanString is syntactically right, but still wrong. Think about it this way: if first character doesn't match then you want to spanString the rest of the string and then return the result with the first character prepended. If the first character does match then you want to return "".

2 Comments

Even if the argument's type is a tuple? Because I'm supposed to pass it a tuple with a char and a list.
@Mike: in that case, that's OK; you then have: spanString :: (Char,String) -> String, though that's rather un-Haskelly.
2

Your type definition is wrong .

   spanString :: Char-> String-> String
   spanString _ [] = []
   spanString c (x:xs) | c==x = []
                       | otherwise = x:spanString c xs

2 Comments

Your answer is correct, but it should be noted that the question was marked as homework and for homework it is common, as explained in the description of the tag, to "guide the student in solving the problem, rather than simply showing the complete answer."
This doesn't completely answer the problem I had, but it helped. The accepted answer and the comments on it are what really got me to getting it.
0

Just for information, utility functions like this can almost always be found in Prelude, or one of the standard libraries. In this case, takeWhile will help:

spanString :: (Char, String) -> String
spanString (c, s) = takeWhile (/= c) s

(i.e., keep taking characters while they don't equal c).

Passing arguments in a tuple is slightly odd, but if that's what's required then so be it.

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.