I'm trying to broaden my mind by learning Haskell.
My self-inflicted homework was to build a clock-tick generator which would give me Poisson-distributed intervals, and the end result (after a long struggle, I admit) was this:
import System.Random
poissonStream :: ( Ord r, Random r, Floating r, RandomGen g) => r -> r -> r -> g -> [r]
poissonStream rate start limit gen
| next > limit = []
| otherwise = next:(poissonStream rate next limit newGen)
where (rvalue, newGen) = random gen
next = start - log(rvalue) / rate
But there are two things (at least) I don't understand:
Why do I need "Ord r
" as well as "Floating r
"? (I would have expected some kind of automatic inheritance: "Floating" implies "Ord".)
By what path is the implied type definition "rvalue :: Float
" achieved?
In GHCi I get what I would have expected:
*Main System.Random> let (rvalue, newGen) = random (mkStdGen 100)
<interactive>:1:23:
Ambiguous type variable `t' in the constraint:
`Random t' arising from a use of `random' at <interactive>:1:23-43
Probable fix: add a type signature that fixes these type variable(s)
rvalue is a loose cannon which I have to tie down:
*Main System.Random> let (rvalue, newGen) = random (mkStdGen 100) :: (Float, StdGen)
*Main System.Random> rvalue
0.18520793
Please be gentle with a Haskell n00b.
Why do I need "Ord r" as well as "Floating r"? (I would have expected some kind of automatic inheritance: "Floating" implies "Ord".)
Floating
is supposed to classify all floating-point numbers, including complex ones. There is no ordering for complex numbers. You could use RealFloat
instead of Floating
, which implies Ord
.
By what path is the implied type definition "rvalue :: Float" achieved?
Well you can infer that from the context in which rvalue
is used. It's the argument to log
, and
:t log
gives
log :: (Floating a) => a -> a
so rvalue
must be in the Floating
class (so it will be "some type in the Floating
typeclass, not precisely Float
). Further, the result of log
is of the same type as its input and is used in a computation with start
and rate
and is compared with limit
which are all of type r
, so rvalue
will be of r
(which is suitable because r
is of Floating
as well).
In your GHCi example, there is no more context. Type
:t random (mkStdGen 100)
This gives you
random (mkStdGen 100) :: (Random a) => (a, StdGen)
GHCi doesn't know what type to fill in for a
here. It only knows it's got to be in typeclass Random
.
In Haskell, Ord and Floating are independent or orthogonal concepts. Perhaps you were assuming that Floating only referred to very specific types, like Double and Float (this would be the case in most other languages)?
As Rudiger pointed out, there are types that are instances of the class Floating that are not Ord because there is no ordering for certain float types, like Complex. Another example besides Complex numbers would be vectors, for which you may define these Floating functions, but not any kind of sensible Ord-ering.
Remember that type classes just specify the set of functions (like an interface in Java or C#, if that's a helpful way of thinking about it) that must be defined for a type a; to be Ord, a type a just needs comparison operators, and to be an instance of Floating, a type a just needs to implement Fractional and have the functions pi, exp, sqrt, log, sin, cos, ... defined for them. That's all Floating means, nothing more, nothing less.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With