Search code examples
haskellioprogram-entry-point

Main function input for RealFloat in haskell


I am trying to make a main function that takes 2 Realfloat input and pass through this function

bmiTell :: (RealFloat a) => a -> a -> String  
bmiTell weight height  
    | bmi <= 18.5 = "You're underweight"
    | bmi <= 25.0 = "You're normal"
    | bmi <= 30.0 = "You're overweight"
    | otherwise = "You're obese"
    where bmi = weight / height ^ 2  

And my solution is, where I trying to get weight and height and pass it through the bmiTell funtion

main = do
    putStrLn "What's your weight?"  
    weight <- getLine  
    putStrLn "What's your height?"  
    height <- getLine
    print (bmiTell( read weight ::Float, read height ::Float))

and this is giving this error

* No instance for (Show ((Float, Float) -> String))
        arising from a use of `print'
        (maybe you haven't applied a function to enough arguments?)
    * In a stmt of a 'do' block:
        print (bmiTell (read weight :: Float, read height :: Float))
      In the expression:
        do putStrLn "What's your weight?"
           weight <- getLine
           putStrLn "What's your height?"
           height <- getLine
           ....
      In an equation for `main':
          main
            = do putStrLn "What's your weight?"
                 weight <- getLine
                 putStrLn "What's your height?"
                 ....
   |
14 |     print (bmiTell( read weight ::Float, read height ::Float))
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

getbmi.hs:14:12: error:
    * No instance for (RealFloat (Float, Float))
        arising from a use of `bmiTell'
    * In the first argument of `print', namely
        `(bmiTell (read weight :: Float, read height :: Float))'
      In a stmt of a 'do' block:
        print (bmiTell (read weight :: Float, read height :: Float))
      In the expression:
        do putStrLn "What's your weight?"
           weight <- getLine
           putStrLn "What's your height?"
           height <- getLine
           ....
   |
14 |     print (bmiTell( read weight ::Float, read height ::Float))
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I understand that RealFloat is Double or Float. Can anyone How to get input for RealFloat and pass it through a function.


Solution

  • For simplicity, I will in the following discuss a monomorphic version of your function, i.e.

    bmiTell :: Double -> Double -> String
    

    (For a RealFloat, the type Double is usually the only sensible choice.)


    Your code would actually work if you had defined

    bmiTell :: (Double, Double) -> String  
    bmiTell(weight, height)
      | ...
    

    That would be the “normal way” to define a multi-parameter function, as most other programming languages would do it. In this case bmiTell (read weight, read height) would work.

    You can do this in Haskell, however we prefer a different style that is in many ways equivalent but offers some nice advantages, though it is often confusing for beginners: we Curry most multi-parameter functions.

    It can be explained quite simply: instead of supplying both arguments at once, in a tuple, you supply first one argument, then the next. And so on. So, you have the function bmiTell. You give it the argument w, i.e. you write bmiTell w. Then you also give it the argument h, so you write (bmiTell w) h, where the parser allows us to omit the parentheses.

    The function has thus the signature bmiTell :: Double -> SOMETHING, where SOMETHING must be able to accept another parameter... and only then give a string. So, in fact

    bmiTell :: Double -> (Double -> String)
    

    Here too, the parsing rules were deliberately chosen so we can actually just omit the parentheses.

    To use that function, you must, well – give both arguments one-by-one.

        bmiTell (read weight) (reight height)
    

    In the whole program:

    main :: IO ()
    main = do
        putStrLn "What's your weight?"  
        weight <- getLine  
        putStrLn "What's your height?"  
        height <- getLine
        print (bmiTell (read weight) (read height))
    

    Actually we like to omit even more parentheses (we're not Lispers!), which can be done by using the handy no-op $ operator:

        print $ bmiTell (read weight) (read height)