Как работает дедукция типа в Haskell?

Я пытаюсь расширить свой разум, изучая Haskell.

Моя собственная домашняя работа заключалась в том, чтобы построить генератор тактовых импульсов, который дал бы мне распределенные по Пуассону интервалы, и конечный результат (после долгой борьбы, я признаю) был следующим:

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  

Но есть две вещи (по крайней мере), которые я не понимаю:

Зачем мне нужно "Ord r", а также "Floating r"? (Я бы ожидал какого-то автоматического наследования: "Плавающий" означает "Орд".)

По какому пути подразумевается подразумеваемое определение типа "rvalue :: Float"? В GHCi я получаю то, что я ожидал:

*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 - свободная пушка, которую я должен привязать:

*Main System.Random> let (rvalue, newGen) = random (mkStdGen 100) :: (Float, StdGen)
*Main System.Random> rvalue
0.18520793

Будьте осторожны с Haskell n00b.

Ответ 1

Зачем мне нужен "Ord r", а также "Floating r"? (Я бы ожидал какого-то автоматического наследования: "Плавающий" означает "Орд".)

Floating должен классифицировать все числа с плавающей запятой, включая сложные. Нет порядка для комплексных чисел. Вы можете использовать RealFloat вместо Floating, что подразумевает Ord.

По какому пути подразумевается подразумеваемое определение типа "rvalue:: Float"?

Ну, вы можете сделать вывод, что из контекста, в котором используется rvalue. Это аргумент log и

:t log

дает

log :: (Floating a) => a -> a

поэтому rvalue должен находиться в классе Floating (так что это будет "некоторый тип в классе Floating, а не точно Float). Кроме того, результат log имеет тот же тип как его вход и используется при вычислении с start и rate и сравнивается с limit, которые все типа r, поэтому rvalue будет r (что подходит, потому что r имеет значение Floating).

В вашем примере GHCi больше нет контекста. Тип

 :t random (mkStdGen 100)

Это дает вам

random (mkStdGen 100) :: (Random a) => (a, StdGen)

GHCi не знает, какой тип заполнить для a здесь. Он знает, что это должно быть в typeclass Random.

Ответ 2

В Haskell орды и плавающие независимы или orthogonal. Возможно, вы предполагали, что Floating относится только к очень конкретным типам, таким как Double и Float (это было бы на большинстве других языков)?

Как указывал Рудигер, существуют типы, которые являются экземплярами класса Floating, которые не являются Ord, потому что не существует порядка для определенных типов float, таких как Complex. Другим примером, кроме комплексных чисел, были бы векторы, для которых вы можете определить эти плавающие функции, но не любые разумные орды.

Помните, что классы типа просто задают набор функций (например, интерфейс в Java или С#, если это полезный способ думать об этом), которые должны быть определены для типа a; для того, чтобы быть Ord, тип a просто нуждается в операторах сравнения и быть экземпляром Floating, тип a просто должен реализовать Fractional и иметь функции pi, exp, sqrt, log, sin, cos,... определены для них. То, что все плавающие означает, не более, не что иное.