Thank does sound like a pain, but it's better than putting fromIntegral all over my code. Why can't Haskell unify a an expected float with an infered int? It seems that this would make life alot easier.
This is my biggest gripe with Haskell, at least for what I do. The numeric class system is good, but it assumes that the sub-classes are distict, where in fact integers are a proper subset of reals, which are a proper subset of complex numbers. Personally, I would like to see module level explicit coersion, similar to the way the default numeric type is handled. Something like:
module Blah where
coerce Int to Double in mean with fromInt
mean :: [Double] -> Double mean x = sum x / length x
So, if the typesystem infers a Double, but finds an Int, the function would be rewritten as if it had been specified as
mean x = sum x / (fromInt.length) x
If you want a global declaration then you could specify
coerce Int to Double with fromInt
--Matthew Donadio (m.p.donadio@ieee.org)
"Matthew Donadio" <m.p.donadio@ieee.org> writes:
Thank does sound like a pain, but it's better than putting fromIntegral all over my code. Why can't Haskell unify a an expected float with an infered int? It seems that this would make life alot easier.
Personally, I think that one of the things that made my life easier with Haskell compared to C++, is the lack of implicit type cast/conversions/coercions. Now, obviously C++ does this in a quite byzantine way, it's possible that a simpler and better system exists. -kzm -- If I haven't seen further, it is by standing in the footprints of giants
G'day all. On Mon, Mar 03, 2003 at 12:10:28PM -0500, Matthew Donadio wrote:
This is my biggest gripe with Haskell, at least for what I do. The numeric class system is good, but it assumes that the sub-classes are distict, where in fact integers are a proper subset of reals, which are a proper subset of complex numbers.
Haskell Integers are not a proper subset of Haskell Floats or Doubles. Haskell does not support real numbers. It's a similar problem with C/C++. The "long" type is almost never a proper subset of "float", but C will nevertheless happily convert it for you without you asking, potentially losing precision in the process. Cheers, Andrew Bromage
I just fluffed the To: field in the header, so my previous message was bounced, I'm resending this ... sorry if it turns up twice. (I also took the opportunity to make an addendum). Yo,
Haskell Integers are not a proper subset of Haskell Floats or Doubles. Haskell does not support real numbers.
I'd just like to add ... Real numbers are not implementable on a digital computer. This is not a "practical" matter of resources, or a design decision in by the language designer, but the fact that any data type each of whose elements are represented in a finite number of bytes can't have more than a countable number of data elements. Integers, and Rationals can be represented. Even the so-called computable-real-numbers, (still countable) don't work as well as you would want, since things such as "is this number bigger than that one" can't always be computed. Add to this the fact that the generic float type does not satisfy algebraic constraints such as associativity, and so can't, from the axiomatic point of view, be considered to be a representation of rational numbers (let alone reals). Basically, from the algebraic perspective, the float type is a messy fudge, and does not fit in nicely with any of the "pure" types. In general the containment Complex ---> Real --> Rational ---> integer ---> whatever. Does not apply in this simple way to numerical types on a computer, because data types are often constructed on the basis of not just what they represent, but also how they represent these things. The computable world (constructive mathematics) is a lot more complicated and open ended than the existential approach. Regards, Bruce. ps: I've made previous comments about this sort of thing: ``Algebraic Conversions'', A mathematical paper introducing a generalisation of homomorphism of universal algebras. In Research Letters in the Information and Mathematical Sciences Vol 2, May 2001. (pub by Massey University New Zealand). see also, \verb|http://www.massey.ac.nz/~wwiims/rlims| Addendum: Personally I think that lattices of extension fields of the rationals give a good model for understanding what is going on with pure numerical types on digital computers, but maybe that's just me?
b.i.mills@massey.ac.nz wrote:
Basically, from the algebraic perspective, the float type is a messy fudge, and does not fit in nicely with any of the "pure" types. In general the containment
I agree with what you said, but I think you may have missed my point. In numeric programming, at least what I am currently working on, numeric types are very often mixed. With all of the syntactic sugar that Haskell has to make the progarmmer's life easier, and programs easier to read, I find it odd that there is no mechanism to control implicit type coersion. The two biggest examples I can think of are Int to Float (or Double) and Float to Complex Float. Consinder a direct translation of a discrete Fourier transform
dft :: Array Int (Complex Double) -> Array Int (Complex Double) dft a = dft' a w n where w = listArray (0,n-1) [ cis (-2 * pi * fromIntegral i / fromIntegral n) | i <- [0..(n-0)] ] n = snd (bounds a) + 1
dft' :: Array Int (Complex Double) -> Array Int (Complex Double) -> Int -> Array Int (Complex Double) dft' a w n = listArray (0,n-1) [ sum [ a!k * wik i k | k <- [0..(n-1)] ] | i <- [0..(n-1)] ]) where wik i k = w!(i*k `mod` n)
In the defintion of w, the fromIntegrals serve no purpose other than to make everything type. This seems to go against the merits of declarative programming. Also consider the multiplication of a Complex Double by a Double. There are two ways to do this. One is to make the scalar a complex, but this turns what should be two multiplications into four mults and two adds if the zero valued imaginary term doesn't get optimized out.
scale1 :: Complex Double -> Double -> Complex Double scale1 z x = z * (x :+ 0)
The second way to handle this is to expand the complex term into real and imaginary parts, and do the multiplication.
scale2 :: Complex Double -> Double -> Complex Double scale2 (zr :+ zi) x = (zr * x) :+ (zi * x)
Again, these two methods are needed only to make the functions type. I am not advocating C / FORTRAN style type coersion. What I am wishing for is a per module or per function mechanism for giving the programmer a way to state how coersion should take place, with the default being none. A new language feature would be great, but I would settle for a pragma. -- Matthew Donadio (m.p.donadio@ieee.org)
I agree with what you said, but I think you may have missed my point.
Sounds likely on both counts. The same thing annoys me, but my work is in exact or symbolic: -- I don't claim this is a practical example -- I'm just saying that it is logically plausible denominator 2 % 3 == 3 denominator 23 == 1 --- only works because 23 might mean 23%1 denominator (floor (23 % 45)) == type error in application. So now I have to say ... denominator $ fromInteger (floor (23 % 45)) Is this the same malarkey that you are complaining about? I don't like using the conversions, so I generally try to find some way to rephrase the problem at a higher level. So, you want some way to define a default manner in which other types can be mapped to the type that is used in the definition of the function? (Do I understand correctly)? I can see merit in this, someone might use, floor xor ceiling, to shoehorn a float into and integer function. Getting different answers. On the other hand, it looks like ad-hoc polymorphism. I mean you are requesting that we can write a function .... myIntFn :: Integer -> Integer and then define a default mapping for each type into integers. So what if I define functions myFloatInt, myRatioIntInt, etc, to be used by default when the "wrong" type is presented as argument. Such that the range of each of these is distinct. In this way myIntFn can actually map Floats and Ratio Ints in totally different ways. That is, such a mechanism is very close to the ad-hoc polymorphism C++ style ... myIntFn :: Integer -> Integer myIntFn x = 2*x myIntFn :: Float -> Float myIntFn x = x*x and so on. Don't get me wrong, I am not fundamentally opposed to ad-hoc polymorphism, in fact ... (Ok, don't get me onto the subject of polymorphism).
In the definition of w, the fromIntegrals serve no purpose other than to make everything type. This seems to go against the merits of declarative programming.
I feel were you are coming from here ... but I wonder if the issue is more the problem that Int is not a subset of Float. That is, the matter that (1 :: Integer) and (1 :: Float) are not represented the same in the computer means that they really are not in a subset hierarchy. A conversion is a non trivial operation (not just a projection) I just don't feel we can miss out this point. We (as programmers) are pretending that the Floats and Integers are a model of reals and integers. But, they are not, so we have to write our program in such a way that we make up for the sense in which they are not. Ultimately, float is what it is ... not a real number. The fact that we can write programs to get a good approximation to certain real results, using floats, shows that we are good at compensating for the distinction. I don't think that it goes against the merits of declarative programming, rather (once again) declarative programming is causing us to have to recognise the reality of the situation. (In this case that real arithmetic is non computable). Mind you, maybe having to recognise the reality is a form of demerit. Regards Bruce. ps: Parametric polymorphism only works because other operators available are ad-hoc polymorphic :p
b.i.mills@massey.ac.nz wrote:
I agree with what you said, but I think you may have missed my point.
Sounds likely on both counts.
Probably. I get confused a lot. :)
So now I have to say ...
denominator $ fromInteger (floor (23 % 45))
Is this the same malarkey that you are complaining about?
Yup. I just looked at some of the code I am workong on. In 2000 lines of DSP code, 85 contain fromIntegral. That is roughly 1 out of every 25, and it doesn't count instances where fromIntegral appears more than once in a line of code. All are needed solely to placate the type system.
So, you want some way to define a default manner in which other types can be mapped to the type that is used in the definition of the function? (Do I understand correctly)?
Yup, and by doing so I would accept the numeric shenanigans that may result. [ I'm not sure what I want to say in response to the points you made in the rest of the message, so there will probably be a part two tomorrow. ] -- Matthew Donadio (m.p.donadio@ieee.org)
participants (4)
-
Andrew J Bromage -
b.i.mills@massey.ac.nz -
ketil@ii.uib.no -
Matthew Donadio