1
votes

I have below code to take the args to set some offset time.

setOffsetTime :: (Ord a, Num b)=>[a] -> b
setOffsetTime [] = 200
setOffsetTime (x:xs) = read x::Int

But compiler says "Could not deduce (b ~ Int) from the context (Ord a, Num b) bound by the type signature for setOffsetTime :: (Ord a, Num b) => [a] -> b

Also I found I could not use 200.0 if I want float as the default value. The compilers says "Could not deduce (Fractional b) arising from the literal `200.0'"

Could any one show me some code as a function (not in the prelude) that takes an arg to store some variable so I can use in other function? I can do this in the main = do, but hope to use an elegant function to achieve this. Is there any global constant stuff in Hasekll? I googled it, but seems not.

I wanna use Haskell to replace some of my python script although it is not easy.

2
Just a remark: in the second equation of setOffsetTime you explicitly state that the result of read x should be an Int. This immediately implies that the result type of your function is Int and not the more general b for Num b. And that is exactly what the compiler tells you (in his own words).chris
Furthermore the type of read is Read a => String -> a, hence its argument (x in your case) must be of type String, but you are using a for Ord a.chris
Also I do not quite understand what your goal is. Could you try to explain it more concretely and give an example?chris
Thanks for the reply, chris. I want a function which can take a string list, then convert the first element to Float, at last output the Float. If the list is empty, output 200.0 @chrisRussj
Well, than you basically already have all you want in your setOffsetTime and just have to correct some types. E.g., drop the Ord a contraint and replace [a] with [String]. Further drop Num b and replace b by Float. And also drop ... :: Int from the last line.chris

2 Answers

6
votes

I think this type signature doesn't quite mean what you think it does:

setOffsetTime :: (Ord a, Num b)=>[a] -> b

What that says is "if you give me a value of type [a], for any type a you choose that is a member of the Ord type class, I will give you a value of type b, for any type b that you choose that is a member of the Num type class". The caller gets to pick the particular types a and b that are used each time setOffsetTime is called.

So trying to return a value of type Int (or Float, or any particular type) doesn't make sense. Int is indeed a member of the type class Num, but it's not any member of the type class Num. According to that type signature, I should be able to make a brand new instance of Num that you've never seen before, import setOffsetTime from your module, and call it to get a value of my new type.

To come up with an acceptable return value, you can only use functions that likewise return an arbitrary Num. You can't use any functions of particular concrete types.

Existential types are essentially a mechanism for allowing the callee to choose the value for a type variable (and then the caller has to be written to work regardless of what that type is), but that's not really something you want to be getting into while you're still learning.

4
votes

If you are convinced that the implementation of your function is correct, i.e., that it should interpret the first element in its input list as the number to return and return 200 if there is no such argument, then you only need to make sure that the type signature matches that implementation (which it does not do, right now).

To do so, you could, for example, remove the type signature and ask ghci to infer the type:

$ ghci
GHCi, version 7.6.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :{
Prelude| let setOffsetTime []       = 200
Prelude|     setOffsetTime (x : xs) = read x :: Int
Prelude| :}
Prelude> :t setOffsetTime
setOffsetTime :: [String] -> Int
Prelude> :q
Leaving GHCi.

$

And indeed,

setOffsetTime          :: [String] -> Int
setOffsetTime []       =  200
setOffsetTime (x : xs) =  read x :: Int

compiles fine.

If you want a slightly more general type, you can drop the ascription :: Int from the second case. The above method then tells you that you can write

setOffsetTime          :: (Num a, Read a) => [String] -> a
setOffsetTime []       =  200
setOffsetTime (x : xs) =  read x

From the comment that you added to your question, I understand that you want your function to return a floating-point number. In that case, you can write

setOffsetTime          :: [String] -> Float
setOffsetTime []       =  200.0
setOffsetTime (x : xs) =  read x

or, more general:

setOffsetTime          :: (Fractional a, Read a) => [String] -> a
setOffsetTime []       =  200.0
setOffsetTime (x : xs) =  read x