В более общем смысле аргументы в Haskell обычно не записываются в форме "foo (x, y, z)". Вместо этого мы пишем «foo x y z». Первый законен: он объединяет аргументы в одно значение (известное как кортеж) и передает его, но в этом нет необходимости.
Сообщение об ошибке пришло со строки
beta = atan2 (y, x).
Это не удалось из-за типа библиотечной функции atan2, которая примерно равна
atan2 :: RealFloat a => a -> a -> a
Это означает, что для любого типа «a», который является экземпляром «RealFloat» (то есть типов «Float» и «Double»), функция «atan2» принимает два из них в качестве аргументов и возвращает новый. Бит «RealFloat a => ...» говорит о том, что в остальной части типа «a» может быть любым типом, который объявлен экземпляром класса RealFloat. «Float» и «Double» являются примерами этих типов. Таким образом, один потенциальный тип для этой функции:
atan2 :: Double -> Double -> Double
Однако то, что вы сделали, относилось к нему как к другому типу:
atan2 :: (Double, Double) -> Double
Это говорит о том, что atan2 принимает единственный аргумент , который является кортежем, содержащим два значения. Средство проверки типов попыталось определить, является ли весь этот кортеж экземпляром "RealFloat" (то есть одним из типов, который мы можем заменить на "a" в типе "atan2"), и обнаружило, что это не так. Таким образом, он выдал сообщение об ошибке, говорящее так.
То, что на самом деле происходит в синтаксисе atan2 yx и в стрелках в сигнатуре типа, раскрывается, когда вы возвращаете неявные скобки. Оператор типа "->" ассоциативно справа, поэтому Атан2 на самом деле:
atan2 :: Double -> (Double -> Double)
(Примечание: для простоты я опускаю бизнес "RealFloat a".) Это говорит о том, что atan2 принимает аргумент и возвращает новую функцию, которая ожидает второй аргумент.
Теперь давайте поместим неявные скобки в вызов. Приложение функции остается ассоциативным, поэтому определение «бета» выглядит так:
beta = (atan2 x) y
Следуя правилу вычисления скобок изнутри, это применяет функцию «atan2» к «x» и в результате получает новую функцию, которая затем применяется к «y», давая в результате «beta». Видите, как тип и выражение отражают друг друга?
Это не просто теоретический трюк: я мог бы написать что-то вроде
myBeta = atan2 x
...
beta = myBeta y
Или даже
betas = map myBeta ys
где "ys" и "betas" - это списки значений. Возможность делать что-то подобное - одна из сильных сторон Хаскелла.