Ratio data constructor

I'm investigating rational numbers with Haskell. This is the source I've found data Ratio a = !a :% !a deriving (Eq) 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) The Ratio data type would seem to be a parameterized type with two parameters of the same type that must be "settled" in that they're not to be lazily dealt with. Then the :% is the data constructor, the : meaning it's a data constructor and not just an operation function. So this could have been data Ratio a = :% !a !a deriving (Eq) correct? But then what confuses me is in reduce, why reduce x y = (x `quot` d) :% (y `quot` d) and not just %? We have :% defined in the data type and then (%) defined as a function. What is going on here? -- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com

... does the last question have to do with a "smart constructor" by chance?
If so, how?
On Tue, Jul 20, 2021 at 3:42 PM Galaxy Being
I'm investigating rational numbers with Haskell. This is the source I've found
data Ratio a = !a :% !a deriving (Eq)
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)
The Ratio data type would seem to be a parameterized type with two parameters of the same type that must be "settled" in that they're not to be lazily dealt with. Then the :% is the data constructor, the : meaning it's a data constructor and not just an operation function. So this could have been
data Ratio a = :% !a !a deriving (Eq)
correct? But then what confuses me is in reduce, why
reduce x y = (x `quot` d) :% (y `quot` d)
and not just %? We have :% defined in the data type and then (%) defined as a function. What is going on here?
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com

Yes, the (%) function is a smart constructor for Data.Ratio because the
(:%) constructor is not exported. Other examples of this smart constructor
technique would be modules like Data.Set or Data.Map.
A smart constructor means that the module that defines the type does not
export its data constructor(s), making the implementation details
opaque, generally because the author wanted to be able to make assumptions
about the implementation that are not enforced by the type system. In this
case, they wanted all Ratio to be in reduced form. This makes many
operations faster or trivial, e.g. implementing Eq only requires comparing
the numerators and denominators. More information about the technique is
here: https://wiki.haskell.org/Smart_constructors
On Tue, Jul 20, 2021 at 2:17 PM Galaxy Being
... does the last question have to do with a "smart constructor" by chance? If so, how?
On Tue, Jul 20, 2021 at 3:42 PM Galaxy Being
wrote: I'm investigating rational numbers with Haskell. This is the source I've found
data Ratio a = !a :% !a deriving (Eq)
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)
The Ratio data type would seem to be a parameterized type with two parameters of the same type that must be "settled" in that they're not to be lazily dealt with. Then the :% is the data constructor, the : meaning it's a data constructor and not just an operation function. So this could have been
data Ratio a = :% !a !a deriving (Eq)
correct? But then what confuses me is in reduce, why
reduce x y = (x `quot` d) :% (y `quot` d)
and not just %? We have :% defined in the data type and then (%) defined as a function. What is going on here?
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners

Yes, I see here
http://support.hfm.io/1.0/api/haskell2010-1.1.2.0/Data-Ratio.html that
the module Data.Ratio exports this
module Data.Ratio (
Ratio, Rational, (%), numerator, denominator, approxRational ) where
which doesn't include :%. But then I see numerator and denominator which
do have :%. But then to use them, this, e.g., won't work
numerator (60 20)
It needs the %
numerator (60 % 20)
So internally, :% is used, but users can never use :%, only %, which sends
things through reduce first. I could write my own version of Ratio that
didn't hide :<some symbol> and that would be okay. Have I got this right?
On Tue, Jul 20, 2021 at 4:37 PM Bob Ippolito
Yes, the (%) function is a smart constructor for Data.Ratio because the (:%) constructor is not exported. Other examples of this smart constructor technique would be modules like Data.Set or Data.Map.
A smart constructor means that the module that defines the type does not export its data constructor(s), making the implementation details opaque, generally because the author wanted to be able to make assumptions about the implementation that are not enforced by the type system. In this case, they wanted all Ratio to be in reduced form. This makes many operations faster or trivial, e.g. implementing Eq only requires comparing the numerators and denominators. More information about the technique is here: https://wiki.haskell.org/Smart_constructors
On Tue, Jul 20, 2021 at 2:17 PM Galaxy Being
wrote: ... does the last question have to do with a "smart constructor" by chance? If so, how?
On Tue, Jul 20, 2021 at 3:42 PM Galaxy Being
wrote: I'm investigating rational numbers with Haskell. This is the source I've found
data Ratio a = !a :% !a deriving (Eq)
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)
The Ratio data type would seem to be a parameterized type with two parameters of the same type that must be "settled" in that they're not to be lazily dealt with. Then the :% is the data constructor, the : meaning it's a data constructor and not just an operation function. So this could have been
data Ratio a = :% !a !a deriving (Eq)
correct? But then what confuses me is in reduce, why
reduce x y = (x `quot` d) :% (y `quot` d)
and not just %? We have :% defined in the data type and then (%) defined as a function. What is going on here?
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com

That sounds about right. numerator and denominator are deconstructors,
which you need to use to access the fields because you can’t pattern match
without access to the :% constructor. If you look at their implementation,
they are deconstructing with a pattern match rather than constructing like
the % function.
On Tue, Jul 20, 2021 at 15:10 Galaxy Being
Yes, I see here http://support.hfm.io/1.0/api/haskell2010-1.1.2.0/Data-Ratio.html that the module Data.Ratio exports this
module Data.Ratio ( Ratio, Rational, (%), numerator, denominator, approxRational ) where
which doesn't include :%. But then I see numerator and denominator which do have :%. But then to use them, this, e.g., won't work
numerator (60 20)
It needs the %
numerator (60 % 20)
So internally, :% is used, but users can never use :%, only %, which sends things through reduce first. I could write my own version of Ratio that didn't hide :<some symbol> and that would be okay. Have I got this right?
On Tue, Jul 20, 2021 at 4:37 PM Bob Ippolito
wrote: Yes, the (%) function is a smart constructor for Data.Ratio because the (:%) constructor is not exported. Other examples of this smart constructor technique would be modules like Data.Set or Data.Map.
A smart constructor means that the module that defines the type does not export its data constructor(s), making the implementation details opaque, generally because the author wanted to be able to make assumptions about the implementation that are not enforced by the type system. In this case, they wanted all Ratio to be in reduced form. This makes many operations faster or trivial, e.g. implementing Eq only requires comparing the numerators and denominators. More information about the technique is here: https://wiki.haskell.org/Smart_constructors
On Tue, Jul 20, 2021 at 2:17 PM Galaxy Being
wrote: ... does the last question have to do with a "smart constructor" by chance? If so, how?
On Tue, Jul 20, 2021 at 3:42 PM Galaxy Being
wrote: I'm investigating rational numbers with Haskell. This is the source I've found
data Ratio a = !a :% !a deriving (Eq)
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)
The Ratio data type would seem to be a parameterized type with two parameters of the same type that must be "settled" in that they're not to be lazily dealt with. Then the :% is the data constructor, the : meaning it's a data constructor and not just an operation function. So this could have been
data Ratio a = :% !a !a deriving (Eq)
correct? But then what confuses me is in reduce, why
reduce x y = (x `quot` d) :% (y `quot` d)
and not just %? We have :% defined in the data type and then (%) defined as a function. What is going on here?
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners

On Tue, Jul 20, 2021 at 1:43 PM Galaxy Being
I'm investigating rational numbers with Haskell. This is the source I've found
data Ratio a = !a :% !a deriving (Eq)
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)
The Ratio data type would seem to be a parameterized type with two parameters of the same type that must be "settled" in that they're not to be lazily dealt with. Then the :% is the data constructor, the : meaning it's a data constructor and not just an operation function. So this could have been
The type has one parameter a, the (:%) constructor has two arguments (both of type a).
data Ratio a = :% !a !a deriving (Eq)
You always need to use parentheses around an infix operator to use it in prefix, just like you need backticks around a function to use it as infix (like `quot` in the implementation of reduce). data Ratio a = (:%) !a !a deriving (Eq)
correct? But then what confuses me is in reduce, why
reduce x y = (x `quot` d) :% (y `quot` d)
and not just %? We have :% defined in the data type and then (%) defined as a function. What is going on here?
The function (%) uses reduce in its definition, the equation would never terminate due to infinite recursion if the function (%) was used instead of the constructor (:%). -bob

The Ratio type is a fraction. If a is Integer, it is an infinite precision
fraction. You can divide it arbitrarily and it will never lose any
precision (though it will take more space).
Reduce is designed to create a fraction from two numbers, not solve it. so
if you supply 5 and 10, it will return a Ratio 1/2, because 5 is the
greatest common denominator.
The % operator appears to take arbitrary numbers but it seems designed to
intend to keep the negative sign on the numerator, so as to maintain some
invariant in the data.
On Tue, Jul 20, 2021 at 4:43 PM Galaxy Being
I'm investigating rational numbers with Haskell. This is the source I've found
data Ratio a = !a :% !a deriving (Eq)
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)
The Ratio data type would seem to be a parameterized type with two parameters of the same type that must be "settled" in that they're not to be lazily dealt with. Then the :% is the data constructor, the : meaning it's a data constructor and not just an operation function. So this could have been
data Ratio a = :% !a !a deriving (Eq)
correct? But then what confuses me is in reduce, why
reduce x y = (x `quot` d) :% (y `quot` d)
and not just %? We have :% defined in the data type and then (%) defined as a function. What is going on here?
-- ⨽ Lawrence Bottorff Grand Marais, MN, USA borgauf@gmail.com _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
participants (3)
-
Bob Ippolito
-
David McBride
-
Galaxy Being