tags:

views:

157

answers:

4

Hello,

I get input (x) from user, convert it to Int by let y = (read x)::Int and then I would like the function to behave in a special way if user gave nothing (empty string).

-- In this place I would like to handle situation in which user
-- gave empty string as argument
-- this doesnt work :/
yearFilter [] y = True

--This works fine as far as y is integer
yearFilter x y  | x == (objectYear y) = True
                | otherwise = False

Thanks for help, Bye

+3  A: 

There is no NULL unless you explicitly define it. You could check for empty strings like this.

readInput :: IO ()
readInput = do
   ln <- getLine
   if valid ln
      then -- whatever
      else -- whatever

valid x
    | null x                = False
    | not istJust convert x = False
    | otherwise             = True
where convert :: String -> Maybe Int
      convert = fmap fst $ listToMaybe  . reads $ "f"
LukeN
+2  A: 

The 'read' function cannot convert an empty string to an int, and will cause an error if you try to do so. You'll need to test whether the input is an empty string before converting to int. If you want to use a default value (such as 0) in the event that the user enters an empty string, you could do something like this:

let y = if null x then 0 else read x
Daniel Pratt
Please, don't teach people `length x == 0`. The idiomatic and performant way to write this test is `null x`.
Norman Ramsey
As to being idiomatic, point taken. Given the definition of 'length', I can't believe there'd be any difference in perf. In any case, I'm a bit surprised someone felt the need to down-vote.
Daniel Pratt
"length" will walk the string to the very end in order to find out the length, and if that string is 400203209 Characters, you've got a performance issue. "null" will run in constant time, because it just needs to see if at least ONE element is in there.
LukeN
Ah, your right. I was focusing on the matching case. I feel a bit ashamed, as one of my pet peeves happens to be 'IF ((SELECT COUNT (*) ...) > 0) BEGIN...' vs. 'IF EXISTS (SELECT ...) BEGIN...'. Same issue!
Daniel Pratt
+12  A: 

Perhaps you want a Maybe type? If the user enters the empty string, your function returns Nothing; otherwise it returns Just n, where n is what's entered by the user?

userInt :: String -> Maybe Int
userInt [] = Nothing
userInt s  = Just $ read s

(I haven't compiled this code.)

Norman Ramsey
+2  A: 

In this case, Maybe may not suffice: You have three conditions to worry about:

  1. The user entered nothing
  2. The user input was valid
  3. The user input was unparsable

This data type and function express this directly:

data Input a = NoInput | Input a |  BadInput String
    deriving (Eq, Show)

input :: (Read a) => String -> Input a
input "" = NoInput
input s =
    case filter (null.snd) (reads s) of
        ((a,_):_) -> Input a
        otherwise -> BadInput s

Note that rather than using the incomplete function read, it uses reads which will not error on input which cannot be converted. reads has a somewhat awkward interface, alas, so I almost always end up wrapping it in a function that returns Maybe a or something like this here.

Example use:

> input "42" :: Input Int
Input 42
> input "cat" :: Input Int
BadInput "cat"
> input "" :: Input Int
NoInput

I would code your yearFilter function like this:

yearFilter :: Maybe Int -> Int -> Bool
yearFilter Nothing  _ = True
yearFilter (Just x) y = x == objectYear y

Then I'd handle user input as:

inputToMaybe :: Input a -> Maybe a
inputToMaybe (Input a) = Just a
inputToMaybe _         = Nothing

do
    a <- input `fmap` getLine
    case a of
        BadInput s -> putStrLn ("Didn't understand " ++ show s)
        otherwise  -> ... yearFilter (inputToMaybe a) ....

N.B.: I've cleaned up the code in yearFilter a bit: no need to use guards to produce a boolean from a test - just return the test, function application (objectYear) binds tighter than operators (==) so removed parenthesis, replaced names of unused inputs with _.


Okay, I admit I can't help myself.... I've rewritten yearFilter yet again, this time as I would be inclined to write it:

yearFilter :: Maybe Int -> Int -> Bool
yearFilter x y = maybe True (== objectYear y) x

Learning about Maybe and maybe was first thing about Haskell that really made me love the language.

MtnViewMark