tags:

views:

137

answers:

2

In C# I can declare the following

class A {
    int Field;
}

class B : A {
    int Field2;
}

static int f(A a) { return a.Field; }
static int f(B b) { return a.Field + b.Field2; }

static void Main(string[] args) {
    A a = new A() { Field = 1 };
    A b = new B() { Field = 1, Field = 2};

    Console.WriteLine(f(a) + f(b));
}

In Haskell I would type out the above as

data A = A { field :: Int } | B { field :: Int, field2 :: Int }

f :: A -> Int
f (A a) = a
f (B a b) = a + b

main :: IO()
main = do putStrLn $ show (f(a) + f(b))
    where a = A 1
          b = B 1 2

What I don't like about the Haskell counterpart is that I have to repeat field twice in the data definition of A (this becomes more tiresome as the number of fields increases that are present in A that need to be in B). Is there a more concise way in Haskell to write B as a subclass of A (that is somewhat similar to the C# way)?

+5  A: 

If A has a lot of fields, one design that might make sense is to have two different datatypes for A and B, where a B contains an A, and then use a typeclass to define f. Like so:

data A = A {field1 :: Int, field2 :: Int, ..., field9999 :: Int}
data B = B {a :: A, field10000 :: Int}

class ABC a where
    f :: a -> Int

instance ABC A where
    f a = field1 a + field101 a

instance ABC B where
    f b = f (a b) + field10000 b
sepp2k
Yeah, this is the way to do it, I'd say. Note also that `f` in the C# code was an *overloaded function*. Completely unrelated to inheritance, overloaded functions will *always* need to be defined as a type class. Haskell's type classes have more in common with function overloading than they do with OOP "classes", in fact.
camccann
@seppk- I presently have this compositional approach in my code. I was hoping that I could avoid it, but I think it will have to do in the mean time. Thanks for the answer.@camccann, the functions were provided as an example what what would be done with the instances and the overloading aspect wasn't the focus of the post, nonetheless thanks for your insights on the relationship between the two languages.
lewellen
+1  A: 

Your data definition is a lateral move, both the A and B constructors are instances of the type A, where what you're looking for is a type B that is-a type A but not just an instance of type A.

Try:

data Foo = Foo {field :: Type}
newtype Bar = Bar Foo

fromBar Bar x = x
toBar x = Bar x

This will allow you to declare all sorts of specialty functions on type Bar that will not apply to Foo, as well as operate on Bar using functions designed for type Foo.

If you want to extend the data parameters of Foo with additional information in Bar, you could use a data declaration and a has-a relationship. This isn't as desirable as a newtype, as newtype is lazily evaluated where the data declaration is strict. Still:

data Foo = Foo {field :: Type}
data Bar = Bar {fooField :: Foo, moreField :: Type}

As sepp2k points out, what you really want to do to define the "nature" of a type is not by how it looks, but by how it works. We do this in Haskell by defining a type class, and having a type instance that class.

Hope this helps.

Dan
@dan thanks for the alternative view of how to go about this. I see now the difference in mental models between the two languages for achieving the objective.
lewellen
np, explaining things helps me understand them better
Dan