As already said, random numbers can't really be pure values1.
However, this doesn't really need to bother you. Just look at it the other way around: other languages simply don't have such a thing as pure values, it's always states with real-world interference you're dealing with. Haskell can do that as well, in the IO monad. You don't need to know how exactly that works, just imitate what it would look like in a procedural language (there are a few pitfalls here, though).
First of all you need some algorithm, that doesn't have anything to do with the language whatsoever. The obvious way is to accumulate the probabilities across the list, and use the resulting step-function as a map from [0, 1[ to your desired values.
probsListLookup :: [(Double, a)] -> Double -> a
probsListLookup pAssoc = look acc'dList
where acc'dList = scanl1 (\(pa,_) (pn,x) -> (pa+pn,x)) pAssoc
look ((pa, x) : pas) rval
| rval < pa = look pas rval
| otherwise = x
Note that this neither handles invalid inputs well (probabilities not summing to 1 etc.) nor is it efficient, scrambling O (n) through acc'dList for each requested value2. More important though, note that it's a pure function! It's generally a good idea to use pure functions as much as possible, and only go into IO when it's absolutely necessary. Like now: we need to obtain a single Double value between 0 and 1. Easy!
main = do
lookupVal <- randomRIO (0, 1)
print $ probsListLookup [(0.1, 1), (0.2, 2), (0.3, 4), (0.4, 5)] lookupVal
1At least not of a basic type like Int; you could actually do "pure computations" on whole probabilty distributions though. Doing that explicitly is very cumbersome, but Haskell allows you to use specific monads (or in fact comonads) to make that just as easy as it is in Haskell IO (or in any impure language) but without the dangers of input/output.
2You could improve that e.g. with Data.Map.