11

A question just came up about java generics. The example code is:

public interface A < T extends A < T> > {

}  

A linked question asks about

Class Enum<E extends Enum<E>> ...

When I try to read the java documentation about these kinds of generic expressions, my eyes glaze over and it's Greek to me.

I'm hoping that I can understand them in terms of the Haskell equivalent!

What is the equivalent (or similar) in Haskell of one or both of these examples?

Community
  • 1
  • 1
Matt Fenwick
  • 48,199
  • 22
  • 128
  • 192
  • 1
    You can't really compare imperative and functional languages. They just don't translate really well into each other. – marco-fiset Nov 18 '11 at 19:14
  • 4
    @marcof -- this isn't about FP vs imperative, this is about type systems. I've read that (but forgot where) that Java got its inspiration for generic types from Haskell's type system, and I think (and hope) I understand Haskell's a bit better. – Matt Fenwick Nov 18 '11 at 19:16
  • Haskell doesn't have subtyping, so it's difficult to explain a nuance with Java's `extends` keyword in terms of Haskell. – Dan Burton Nov 18 '11 at 20:18

3 Answers3

10

This trick is used to allow the interface to refer to the concrete implementation type, for example to enforce that the type of the argument and the type of the result are the same type as the implementing class in something like this:

public interface Num<A extends Num<A>> {
    A add(A other); 
}

This is similar to what you get for free with type classes in Haskell:

class Num a where
    (+) :: a -> a -> a
hammar
  • 138,522
  • 17
  • 304
  • 385
  • The part that was confusing me as the `T extends A < T>` -- so basically, that just doesn't even appear in the Haskell version? Cool! Is `parametric polymorphism` the correct term for this? – Matt Fenwick Nov 18 '11 at 19:20
  • 3
    This isn't really equivalent because the Java version allows me to define a type `Foo implements Num`, while the Haskell version would only allow `Foo implements Num`. – sepp2k Nov 18 '11 at 19:28
  • @sepp2k: Yes, it's an approximation. I don't think you can get the equivalent of the Haskell one in Java. – hammar Nov 18 '11 at 19:43
  • 2
    @sepp2k you *could* do that, but it seems that the typical usage of this Java pattern is to accomplish what hammar has described. – Dan Burton Nov 18 '11 at 20:24
  • @MattFenwick `parametric polymorphism` refers to all of generics. I.e. whenever you see `Foo` that means that `Foo` is polymorphic by parameterization. – Erick G. Hagstrom Dec 30 '15 at 13:27
3

This is interesting, as this has perplexed me also. Let's try to model it. I will probably not end up with the same thing this idiom is used for in Java.

If a type A inherits from a type B, in functional land that means that there is a function B -> A. Do not worry about the difference between classes and interfaces at this point; there is little difference in the functional translation (an interface is just a record of functions). Let's do a non-recursive translation to get a feel for it:

interface Showable {
    string show();
}

interface Describer<T extends Showable> { }

Translating to records of functions:

data Showable = Showable { show :: String }

data Describer t = Describer { showable :: t -> Showable }

If we forget about downcasting, then if we have some object in Java such that all we know about it is that it is a Showable, then it corresponds to having a Showable object in Haskell. On the surface, passing a Showable and passing a string feel like different things, but they are equivalent.

The extends Showable constraint enters in that if we have a Describer t then we know that t "is" Showable; i.e. there exists a function t -> Showable.

makeDescriber :: (t -> Showable) -> Describer t
makeDescriber f = Describer { showable = f }

Now let's go to hammar's exmaple, incorporating polymorphism.

interface Number<A extends Number<A>> {
    A add(A other); 
}

translating to a record of functions

data Number a = Number {
    add :: a -> a,
    number :: a -> Number a
}

So now if we have a Number a, then we know that a "is" a Number a; i.e. there is a function a -> Number a.

Instances of the java interface Number become functions on a type.

intNumber :: Integer -> Number Integer
intNumber x = Number { add = \y -> x + y, number = intNumber }

This function corresponds to a class Integer extends Number<Integer>. If we have two integers x and y, we can add them using this "OO" style:

z :: Integer -> Integer -> Integer
z x y = intNumber x `add` y

And what about the generic function:

T Add< T extends Number<T> >(T x, T y) { return x.add(y); }

(hmm is that correct Java syntax? My experience of this style is from C#)

Remember the constraints become functions, so:

add' :: (t -> Number t) -> t -> t -> t
add' n x y = n x `add` y

Of course, in Haskell, we see how bundling an object together with the operations it supports is kind of convoluted, so we prefer to separate them:

data Num t = Num { add :: t -> t -> t }

add' :: Num t -> t -> t -> t
add' n x y = add n x y

And we instantiate the Num dictionary with the actual operations, eg.

integerNum :: Num Integer
integerNum = Num { add = (+) }

Typeclasses are just a bit of syntactic sugar over this latter idea.

Maybe that helps? I just wanted to see how it would translate literally.

luqui
  • 59,485
  • 12
  • 145
  • 204
  • +1 The last part is not clear though. Where does the actual addition take place in `Num t`? and why was it defined in the first place? – is7s Nov 18 '11 at 22:25
  • @is7s, added an instantiation of `Num Integer`. – luqui Nov 18 '11 at 22:58
-1

I don't know if there is an equivalent statement for Haskell, but Haskell does have typeclasses. For example, Show is a typeclass, and many objects "extend" or "implement" show, that is, you can "show 3", "show [1,2,3,4]", etc.

NoBugs
  • 9,310
  • 13
  • 80
  • 146