The numeric c types are (effectively) integral, too.

This query is regarding Foreign.C.Types http://hackage.haskell.org/packages/archive/base/4.6.0.1/doc/html/Foreign-C-.... Now that the constructors for CClock, CTime, CUSeconds, and CSUSeconds are exposed, it's become clear that these are Ints or Words. Technically on some architectures they could be floating point, but I would bet that on all architectures that Haskell is ever used on, none of these are floating point types. Adding Bounded, Enum, Integral, and Bits instances to the "numeric" types would break nothing and ease situations such as this: roundTo :: CTime -> CTime -> CTime roundTo a b = (a `div` b) * b This works right now, but requires a trivial Integral class be written for every program that wants to do it. instance Integral CTime where quot (CTime a) (CTime b) = CTime (a `quot` b) rem (CTime a) (CTime b) = CTime (a `rem` b) div (CTime a) (CTime b) = CTime (a `div` b) mod (CTime a) (CTime b) = CTime (a `mod` b) quotRem (CTime n) (CTime d) = (\(d,m) -> (CTime d, CTime m)) (quotRem n d) divMod (CTime n) (CTime d) = (\(d,m) -> (CTime d, CTime m)) (divMod n d) toInteger (CTime t) = toInteger t I think this and the other trivial instances should just go right into Foreign.C.Types for all the "numeric" types in Foreign.C.Types. Any thoughts to the contrary? Jeff

On Wed, 27 Mar 2013, Jeff Shaw wrote:
roundTo :: CTime -> CTime -> CTime roundTo a b = (a `div` b) * b
This works right now, but requires a trivial Integral class be written for every program that wants to do it.
Writing orphan instances is generally a bad idea. Instead you could put your roundTo function into a nice package and import it everywhere. I don't think that an Integral instance is the right way to go, since it would imply the signature: div :: CTime -> CTime -> CTime and the quotient of two time values is not a time.

On Wednesday, March 27, 2013 11:30:24 AM, Henning Thielemann wrote:
Writing orphan instances is generally a bad idea. Instead you could put your roundTo function into a nice package and import it everywhere.
Yup! So let's put the Integral, etc. instances right in Foreign.C.Types. That way no one will make the mistake you're accusing me of.
I don't think that an Integral instance is the right way to go, since it would imply the signature:
div :: CTime -> CTime -> CTime
and the quotient of two time values is not a time.
That is irrelevant. CTime is a newtype of an integer type, and the quotient of two integers is an integer. Foreign.C.Types certainly doesn't have a problem dividing even stranger things, like graphemes by graphemes. I'm quite sure that the reason that CTime doesn't have an Integral instance is that technically the C spec allows time_t to be a float. However, since time_t is practically always an integer value, we should expose that by defining the Integral instance right in Foreign.C.Types. I looked through the header files for FreeBSD's platforms, and time_t is always an __int32 or __int64. Maybe Linux or NetBSD can provide an exception. Jeff

On Wed, 27 Mar 2013, Jeff Shaw wrote:
On Wednesday, March 27, 2013 11:30:24 AM, Henning Thielemann wrote:
I don't think that an Integral instance is the right way to go, since it would imply the signature:
div :: CTime -> CTime -> CTime
and the quotient of two time values is not a time.
That is irrelevant.
It is highly relevant and certainly a major difference between Haskell and C. A time can be represented with any unit and with either integer or floating or fixed-point numbers. Actually, the integer can always be read as fixed-point number, since the unit is somehow arbitrary. The quotient of two time values is not a time value but a scalar value and it is good if Haskell alerts you when you mix them up.

On 3/27/2013 12:16 PM, Henning Thielemann wrote:
On Wed, 27 Mar 2013, Jeff Shaw wrote:
On Wednesday, March 27, 2013 11:30:24 AM, Henning Thielemann wrote:
I don't think that an Integral instance is the right way to go, since it would imply the signature:
div :: CTime -> CTime -> CTime
and the quotient of two time values is not a time.
That is irrelevant.
It is highly relevant and certainly a major difference between Haskell and C. A time can be represented with any unit and with either integer or floating or fixed-point numbers. Actually, the integer can always be read as fixed-point number, since the unit is somehow arbitrary. The quotient of two time values is not a time value but a scalar value and it is good if Haskell alerts you when you mix them up.
The functionality you want is located in Data.Time.Clock. Foreign.C.Types is for working with C values, which do permit integer division on time_t when it happens to be an integer, which as far as Haskell is concerned, is always. Your argument can also be used to say that C values with the unit "grapheme" shouldn't be allowed division, either. However, they are. Do you think we should remove the Integral instance for CChar? What about the Bounded and Bits instances? Data.Char.Char only has Bounded. Furthermore, CTime is an instance of Num, which means that you can do multiplication on CTime values. Do you think we should remove that? After all, Time * Time is Time ^2, not Time. Jeff

On Wed, 27 Mar 2013, Jeff Shaw wrote:
Your argument can also be used to say that C values with the unit "grapheme" shouldn't be allowed division, either. However, they are. Do you think we should remove the Integral instance for CChar?
Would be better.
Furthermore, CTime is an instance of Num, which means that you can do multiplication on CTime values. Do you think we should remove that? After all, Time * Time is Time ^2, not Time.
If someone had asked me, I would have reject it.

I second this. The fact that time_t is sometimes uint or anything else is simply implementation detail (unless one can point to relevant standards that say otherwise). If one wishes to bet on this being true then simple private newtype with proper instances seems like the way to go. But it feels insecure to do otherwise. Cheers, Krzysztof Skrzętnicki On Wed, Mar 27, 2013 at 5:16 PM, Henning Thielemann < lemming@henning-thielemann.de> wrote:
On Wed, 27 Mar 2013, Jeff Shaw wrote:
On Wednesday, March 27, 2013 11:30:24 AM, Henning Thielemann wrote:
I don't think that an Integral instance is the right way
to go, since it would imply the signature:
div :: CTime -> CTime -> CTime
and the quotient of two time values is not a time.
That is irrelevant.
It is highly relevant and certainly a major difference between Haskell and C. A time can be represented with any unit and with either integer or floating or fixed-point numbers. Actually, the integer can always be read as fixed-point number, since the unit is somehow arbitrary. The quotient of two time values is not a time value but a scalar value and it is good if Haskell alerts you when you mix them up.
______________________________**_________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/**mailman/listinfo/librarieshttp://www.haskell.org/mailman/listinfo/libraries

Krzysztof Skrzętnicki
I second this. The fact that time_t is sometimes uint or anything else is simply implementation detail (unless one can point to relevant standards that say otherwise). If one wishes to bet on this being true then simple private newtype with proper instances seems like the way to go. But it feels insecure to do otherwise.
+1 -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk

On 3/27/13 12:16 PM, Henning Thielemann wrote:
On Wed, 27 Mar 2013, Jeff Shaw wrote:
On Wednesday, March 27, 2013 11:30:24 AM, Henning Thielemann wrote:
I don't think that an Integral instance is the right way to go, since it would imply the signature:
div :: CTime -> CTime -> CTime
and the quotient of two time values is not a time.
That is irrelevant.
It is highly relevant and certainly a major difference between Haskell and C. A time can be represented with any unit and with either integer or floating or fixed-point numbers. Actually, the integer can always be read as fixed-point number, since the unit is somehow arbitrary. The quotient of two time values is not a time value but a scalar value and it is good if Haskell alerts you when you mix them up.
+1. -- Live well, ~wren

On Wed, Mar 27, 2013 at 12:08 PM, Jeff Shaw
I looked through the header files for FreeBSD's platforms, and time_t is always an __int32 or __int64. Maybe Linux or NetBSD can provide an exception.
There have been times when it was a struct made of multiple integral values, also. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On 3/27/2013 12:16 PM, Brandon Allbery wrote:
On Wed, Mar 27, 2013 at 12:08 PM, Jeff Shaw
mailto:shawjef3@gmail.com> wrote: I looked through the header files for FreeBSD's platforms, and time_t is always an __int32 or __int64. Maybe Linux or NetBSD can provide an exception.
There have been times when it was a struct made of multiple integral values, also.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com mailto:allbery.b@gmail.com ballbery@sinenomine.net mailto:ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net That's fascinating. If we're concerned with those cases in base, we should probably remove at least the Enum, Num, and Real instances from CTime, or provide special cases. I imagine Show and Read become nontrivial, as well.
Jeff

2013/3/27 Henning Thielemann
Writing orphan instances is generally a bad idea. Instead you could put your roundTo function into a nice package and import it everywhere. I don't think that an Integral instance is the right way to go, since it would imply the signature:
div :: CTime -> CTime -> CTime
and the quotient of two time values is not a time.
One way to think of it is, CTime is time and should follow the rules of the time. Another way is, CTime is a C type and should follow the rules of C types. The latter perspective seems more appropriate for Foreign.C.* (we are likely to seek out some other module for modelling time). -- Jason Dusek pgp // solidsnack // C1EBC57DC55144F35460C8DF1FD4C6C1FED18A2B

2013/3/28 Jason Dusek
One way to think of it is, CTime is time and should follow the rules of the time. Another way is, CTime is a C type and should follow the rules of C types. The latter perspective seems more appropriate for Foreign.C.* (we are likely to seek out some other module for modelling time).
I agree that the latter perspective seems more appropriate, however, according to Wikipedia : ISO C defines time_t as an arithmetic type, but does not specify any particular type, range, resolution, or encoding for it. Also unspecified are the meanings of arithmetic operations applied to time values. So, even if, in C, you may be able to apply arithmetic operations to a time_t, you can't be sure it makes sense, so you shouldn't normally do it. Why then, make it easier in Foreign.C to do something meaningless ? That's just begging to shoot yourself in the foot.

On Thu, Mar 28, 2013 at 8:20 AM, David Virebayre wrote: So, even if, in C, you may be able to apply arithmetic operations to a
time_t, you can't be sure it makes sense, so you shouldn't normally do
it. Why then, make it easier in Foreign.C to do something meaningless
? That's just begging to shoot yourself in the foot. Because you need to convert it from a foreign representation, which may
require arbitrary math depending on the details of that representation.
And, while there may not be a strict meaning applied by the standard, there
*is* a platform-dependent strict meaning (else times would be useless)
*and* the conversion between the FFI and Haskell representations will need
to know that platform-dependent meaning so that it can do a meaningful
conversion.
--
brandon s allbery kf8nh sine nomine associates
allbery.b@gmail.com ballbery@sinenomine.net
unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

2013/3/28 Brandon Allbery
Because you need to convert it from a foreign representation, which may require arbitrary math depending on the details of that representation. And, while there may not be a strict meaning applied by the standard, there *is* a platform-dependent strict meaning (else times would be useless) *and* the conversion between the FFI and Haskell representations will need to know that platform-dependent meaning so that it can do a meaningful conversion.
Wouldn't it be safer, and more portable, to use either : call ctime, and get a string you can parse call localtime, get back a struct tm which has int members you can work with.

2013/3/28 David Virebayre
2013/3/28 Jason Dusek
One way to think of it is, CTime is time and should follow the rules of the time. Another way is, CTime is a C type and should follow the rules of C types. The latter perspective seems more appropriate for Foreign.C.* (we are likely to seek out some other module for modelling time).
I agree that the latter perspective seems more appropriate, however, according to Wikipedia :
ISO C defines time_t as an arithmetic type, but does not specify any particular type, range, resolution, or encoding for it. Also unspecified are the meanings of arithmetic operations applied to time values.
So, even if, in C, you may be able to apply arithmetic operations to a time_t, you can't be sure it makes sense, so you shouldn't normally do it. Why then, make it easier in Foreign.C to do something meaningless ? That's just begging to shoot yourself in the foot.
Working with C will always involve a lot of easy ways to shoot off a foot or two. Why introduce ob/abstractions that can't really move the needle, since C is so basically unsafe? -- Jason Dusek pgp // solidsnack // C1EBC57DC55144F35460C8DF1FD4C6C1FED18A2B

On Thu, Mar 28, 2013 at 6:32 AM, Jason Dusek
One way to think of it is, CTime is time and should follow the rules of the time. Another way is, CTime is a C type and should follow the rules of C types. The latter perspective seems more appropriate for Foreign.C.* (we are likely to seek out some other module for modelling time).
Phrasing this perhaps more clearly: the point of the FFI types is to reflect foreign types. This may well include doing things which are legal in the foreign type but not necessarily sensible for the conceptual type --- and that may even be necessary in order to, say, convert that foreign type to something more appropriate for Haskell. It ain't pretty; but it's the FFI, it's not going to be pretty. Or well behaved (just in case an interface that requires us to expose unsafePerformIO didn't make that clear...). -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Thu, 28 Mar 2013, Brandon Allbery wrote:
On Thu, Mar 28, 2013 at 6:32 AM, Jason Dusek
wrote: One way to think of it is, CTime is time and should follow the rules of the time. Another way is, CTime is a C type and should follow the rules of C types. The latter perspective seems more appropriate for Foreign.C.* (we are likely to seek out some other module for modelling time). Phrasing this perhaps more clearly: the point of the FFI types is to reflect foreign types.
If CTime is only for interfacing with C, then there should not be any arithmetic or bit manipulation class instances of it, only some conversion functions between CTime and Haskell time representations.

On Thu, Mar 28, 2013 at 4:49 PM, Henning Thielemann < lemming@henning-thielemann.de> wrote:
On Thu, 28 Mar 2013, Brandon Allbery wrote:
On Thu, Mar 28, 2013 at 6:32 AM, Jason Dusek
wrote: One way to think of it is, CTime is time and should follow the rules of the time. Another way is, CTime is a C type and should follow the rules of C types. The latter perspective seems more appropriate for Foreign.C.* (we are likely to seek out some other module for modelling time). Phrasing this perhaps more clearly: the point of the FFI types is to reflect foreign types.
If CTime is only for interfacing with C, then there should not be any arithmetic or bit manipulation class instances of it, only some conversion functions between CTime and Haskell time representations.
...and implementing those should be as painful as possible? -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Thu, 28 Mar 2013, Brandon Allbery wrote:
If CTime is only for interfacing with C, then there should not be any arithmetic or bit manipulation class instances of it, only some conversion functions between CTime and Haskell time representations.
...and implementing those should be as painful as possible?
Implementing these conversion functions means unpacking data from the CTime constructor and convert it to Haskell time representations or vice versa. I don't think it is possible to write conversion functions using Num and Bits that work on all architectures. If there is functionality that can be used on multiple architectures this should be provided as helper functions.

On Thu, Mar 28, 2013 at 5:11 PM, Henning Thielemann < lemming@henning-thielemann.de> wrote:
On Thu, 28 Mar 2013, Brandon Allbery wrote:
If CTime is only for interfacing with C, then there should not be any arithmetic or bit manipulation class instances of it, only some conversion functions between CTime and Haskell time representations.
...and implementing those should be as painful as possible?
Implementing these conversion functions means unpacking data from the CTime constructor and convert it to Haskell time representations or vice versa. I don't think it is possible to write conversion functions using Num and Bits that work on all architectures. If there is functionality that can be used on multiple architectures this should be provided as helper functions.
Can't be done on all architectures, therefore we should ensure it can't be done at all. Which means CTime can never actually be used from Haskell, not even by converting it to a Haskell-friendly representation, but only as an opaque blob; that's really helpful when interfacing to e.g. POSIX functions. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On 3/28/2013 5:11 PM, Henning Thielemann wrote:
On Thu, 28 Mar 2013, Brandon Allbery wrote:
If CTime is only for interfacing with C, then there should not be any arithmetic or bit manipulation class instances of it, only some conversion functions between CTime and Haskell time representations.
...and implementing those should be as painful as possible?
Implementing these conversion functions means unpacking data from the CTime constructor and convert it to Haskell time representations or vice versa. I don't think it is possible to write conversion functions using Num and Bits that work on all architectures. If there is functionality that can be used on multiple architectures this should be provided as helper functions.
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
Please tell us which Haskell compiler targets a platform with a CTime representation that supports Num, but wouldn't support Integral, Bits, or Enum. Or heck, what Haskell target platform's time_t isn't an integer? Jeff

I've formed my proposal in a more formal venue. http://hackage.haskell.org/trac/ghc/ticket/7801 Jeff
participants (8)
-
Brandon Allbery
-
David Virebayre
-
Henning Thielemann
-
Jason Dusek
-
Jeff Shaw
-
Jon Fairbairn
-
Krzysztof Skrzętnicki
-
wren ng thornton