Подтвердить что ты не робот

Как отношение Ratio реализовано в Haskell?

Я кое-что немного смутил, и я не уверен, как я могу больше узнать об этом. Скажем, у меня есть следующая программа:

main :: IO ()
main = do
    x <- liftM read getLine
    y <- liftM read getLine
    print (x % y)

Если я запустил это с помощью ввода 6 и 2, он напечатает 3 % 1.

В какой момент происходит упрощение (а именно деление на gcd)? Он реализован в show? Если это так, то это основное представление рационального еще 6 % 2? Если нет, то делает ли (%) упрощение? У меня создалось впечатление, что (%) является конструктором данных, поэтому как конструктор данных может сделать что-то большее, чем "построить"? Что еще более важно, как я мог бы делать подобные вещи с моими собственными конструкторами данных?

Я ценю любую помощь по этой теме.

4b9b3361

Ответ 1

Ratio фактически реализуется в GHC.Real (очевидно, на GHC) и определяется как

data Ratio a = !a :% !a deriving (Eq)

Чешуйки просто для строгости. Как вы можете видеть, функция % не является конструктором данных, но :% is. Поскольку вы не должны напрямую строить Ratio, вы используете функцию %, которая вызывает сокращение.

reduce ::  (Integral a) => a -> a -> Ratio a
{-# SPECIALISE reduce :: Integer -> Integer -> Rational #-}
reduce _ 0              =  ratioZeroDenominatorError
reduce x y              =  (x `quot` d) :% (y `quot` d)
                           where d = gcd x y
(%) :: (Integral a) => a -> a -> Ratio a
x % y =  reduce (x * signum y) (abs y)

Правило состоит в том, что если оператор начинает с двоеточия :, то он является конструктором, иначе он является просто нормальным оператором. На самом деле, это часть стандарта Haskell, все операторы типа должны иметь двоеточие в качестве своего первого символа.

Ответ 2

Вы можете просто посмотреть источник , чтобы убедиться сами:

instance  (Integral a)  => Num (Ratio a)  where
    (x:%y) + (x':%y')   =  reduce (x*y' + x'*y) (y*y')
    (x:%y) - (x':%y')   =  reduce (x*y' - x'*y) (y*y')
    (x:%y) * (x':%y')   =  reduce (x * x') (y * y')
    negate (x:%y)       =  (-x) :% y
    abs (x:%y)          =  abs x :% y
    signum (x:%_)       =  signum x :% 1
    fromInteger x       =  fromInteger x :% 1 

reduce ::  (Integral a) => a -> a -> Ratio a
reduce _ 0              =  ratioZeroDenominatorError
reduce x y              =  (x `quot` d) :% (y `quot` d)
                           where d = gcd x y