0

I have code that looks a little like the following:

import Data.Complex

data Foo = N Number
         | C ComplexNum

data Number = Int Integer
            | Real Float
            | Rational Rational
     deriving Show

data ComplexNum = con1 (Complex Integer)
                | con2 (Complex Float)
                | con3 (Complex Rational)
     deriving Show

But this seems like a bad way to do it. I would rather have

data Foo = N Number
         | C (Complex Number)

and construct a ComplexNumber with something similar to ComplexNumber $ Real 0.0.

The question is how to make Complex Number possible. Since all of the types in Number have corresponding Complex instances, can I just add deriving Complex to Number?

user3125280
  • 2,779
  • 1
  • 14
  • 23
  • How do you expect to be able to add a `ComplexNum` of form `con2 (Complex Float)` to one of form `con3 (Complex Rational)`? – Tom Ellis Jan 26 '14 at 08:27
  • @Tom by providing those functions myself ("promoting"/converting types, etc) - Number includes all representations of numbers I want like Int, Integer, etc and I will instance fractional for Number and then provide my own complex class with `instance Fractional A => Fractional (Complex a)`. I was hoping someone would know how to do this using Data.Complex instead of my own. – user3125280 Jan 26 '14 at 08:35
  • @user3125280 Why? What would this loss of knowledge and control over type gain you? – not my job Jan 26 '14 at 08:57
  • @chunksOf50 haha that's what I would expect from a haskeller - it is for implementing a weakly typed language (which I'm using as a chance to learn haskell). All number representations have to be compatible for basic arithmetic. (I've solved it myself, so unless you know a better way, don't worry too much :)) – user3125280 Jan 26 '14 at 09:03
  • @user3125280: OK so you have a write an `instance Complex Number` yourself with all these type conversions implemented by hand. You can't expect the compiler to do that for you! – Tom Ellis Jan 26 '14 at 09:47
  • @TomEllis Well actually I can expect a standard library complex class to be able to derive from an instance of fractional (where I put the type conversions) - there is nothing unreasonable about that. Is that not possible? – user3125280 Jan 26 '14 at 10:04
  • (Sorry, I actually mean `instance Num Number`). Anyway, it's rather unclear what you're trying to do. You can form the datatype `Complex Number`. That's fine. It's just pairs of `Number`. It won't have an automatic `Num` instance though unless you provide an instance `RealFloat` for `Number` (an instance which is unlikely to make sense in any case). – Tom Ellis Jan 26 '14 at 11:01
  • @TomEllis Exactly my point (although I used fractional instead of num). I think it's quite clear what I'm trying to do - make my ComplexNumber dependent on Number - and the answer is "You can't without making your own ComplexClass" – user3125280 Jan 26 '14 at 11:08
  • But you *can* do `data Foo = N Number | C (Complex Number)`. I don't know what you mean by "ComplexClass", but I doubt you need that. You *do* need a `Num` instance, but because you have all those type conversions to write by hand anyway, this hardly seems like an additional difficulty. – Tom Ellis Jan 26 '14 at 11:19
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/46124/discussion-between-user3125280-and-tom-ellis) – user3125280 Jan 26 '14 at 23:41

2 Answers2

4

The Haskell approach is to have different types for Complex Float and Complex Int rather than trying to unify them into one type. With type classes you can define all of these types at once:

data Complex a = C a a

instance Num a => Num (Complex a) where
  (C x y) + (C u v) = C (x+u) (y+v)
  (C x y) * (C u v) = C (x*u-y*v) (x*v+y*u)
  fromInteger n = C (fromInteger n) 0
  ...

This at once defines Complex Int, Complex Double, Complex Rational, etc. Indeed it even defines Complex (Complex Int).

Note that this does not define how to add a Complex Int to a Complex Double. Addition (+) still has the type (+) :: a -> a -> a so you can only add a Complex Int to a Complex Int and a Complex Double to another Complex Double.

In order to add numbers of different types you have to explicitly convert them, e.g.:

addIntToComplex :: Int -> Complex Double -> Complex Double
addIntToComplex n z = z + fromIntegral n

Have a look at http://www.haskell.org/tutorial/numbers.html section 10.3 for more useful conversion functions between Haskell's numeric type classes.

Update:

In response to your comments, I would suggest focusing more on the operations and less on the types.

For example, consider this definition:

onethird = 1 / 3

This represents the generic "1/3" value in all number classes:

import Data.Ratio

main = do
    putStrLn $ "as a Double: " ++ show (onethird :: Double)
    putStrLn $ "as a Complex Double: " ++ show (onethird :: Complex Double)
    putStrLn $ "as a Ratio Int: " ++ show (onethird :: Ratio Int)
    putStrLn $ "as a Complex (Ratio Int): " ++ show (onethird :: Complex (Ratio Int))
    ...

In a sense Haskell let's the "user" decide what numeric type the expression should be evaluated as.

ErikR
  • 51,541
  • 9
  • 73
  • 124
  • I was using Data.Complex, sorry - and I made some mistakes in the code too, I'll update it. – user3125280 Jan 26 '14 at 07:03
  • So to elaborate the typeclass Data.Complex exists already and I would like to know if I could avoid writing out ComplexNum somehow. I guess it would most likely involve instancing Complex for number and adding the (+),etc functions. They need to be unified somewhat, and I would like to have complex numbers for any real numbers. – user3125280 Jan 26 '14 at 07:27
  • thanks, I appreciate the answer, but it's not really my question's answer - I only wanted to know if I could use Complex class in Data.Complex. However, this typeclass doesn't have `instance Num a => Num (Complex a) where` (or fractional instead of num in my case) so I can't just instance num/ fractional for my Number type. So the real answer is just 'do it yourself'. – user3125280 Jan 26 '14 at 11:35
0

This doesn't appear to be legal Haskell code. You have three constructors of type ComplexNum all named Complex. Also, data types must begin with a capital letter, so foo isn't a valid type. It's hard to tell what you mean, but I'll take a stab:

If you have a type

data Complex a = (a,a)

you can keep your definition of Number and define foo as:

data Foo = N Number
         | C (Complex Number)
crockeea
  • 21,651
  • 10
  • 48
  • 101