You are missing the point that you can limit the scope of a variable of a quantitative type:
modifyPair :: (Num b, Num c) => (forall a. Num a => a -> a) -> (b, c) -> (b, c) modifyPair f (b, c) = (fb, fc)
Try writing this function without the RankNTypes extension. In particular, allowing the elements of the pair to be different types from each other.
This specific example is not very useful, but a general idea holds. You can specify that the function argument must be polymorphic.
There is an additional trick that you can perform with this snap. The canonical example comes from ST . The purpose of the ST library is to allow limited use of mutable data. That is, you can implement an algorithm that includes a true mutation and present a clean external interface. ST himself will make sure that the use is safe with the help of ingenious system tricks:
newtype ST sa = ...
This additional variable of type s deserves attention. It is displayed in both ST and STRef . Each function that controls STRef ensures that s in ST and STRef are the same type variable. Therefore, when you get to runST , you will find that type s should not be associated with type a . s has a more limited scope than a . The end result of this is that you cannot write something like runST newSTRef . A type controller will reject it because a variable of type s would have to avoid the context in which it was defined.
So there really are some useful tricks that you can do when you can indicate that the argument of the function should be polymorphic.
source share