[Haskell-cafe] Simulating OO programming with type classes; writing a factory fu nction

oleg at pobox.com oleg at pobox.com
Wed Jun 1 02:07:59 EDT 2005

Alistair Bayley wrote:

> There's a small problem: how to write a factory function that returns values
> of various subtypes. The makeSubType function below won't compile, obviously
> because the returns types are different (they're not the same 'm').

Indeed, expressions in both branches of an `if' statement

>   if s == "SubBase1"
>   then SubBase1 3
>   else SubBase2 (SubBase1 4)

must be of the same type. If we had intersection types (I'm not
complaining!), the compiler would have derived the intersection by
itself. As things are now, we have to make the intersection manually:
we have to abstract away irrelevant pieces. Expressions
`SubBase1 3' and `SubBase2 (SubBase1 4)' have in common the fact that
both have types that are instances of a Method class. So, we have to
write that common piece of information explicitly. There are two ways
of doing this, which can be called direct style and CPS style. In
direct style, we do

> data WM = forall m. Method m => WM m
> makeSubType1 :: String -> WM 
> makeSubType1 s =
>   if s == "SubBase1"
>   then WM $ SubBase1 3
>   else WM $ SubBase2 (SubBase1 4)
> test1 = let foo x = case x of WM y -> method2 y
> 	  in map (foo . makeSubType1) ["SubBase1", "SubBase2"]

The CPS style is just the inverse:

> -- Higher-ranked type: signature is required!
> makeSubType2:: (forall m. Method m => m -> w) -> String -> w
> makeSubType2 consumer s =
>   if s == "SubBase1"
>   then consumer $ SubBase1 3
>   else consumer $ SubBase2 (SubBase1 4)
> test2 = let foo x = method2 x
> 	in map (makeSubType2 foo) ["SubBase1", "SubBase2"]

The CPS style involves less tagging (no need to add and remove the tag
WM). Also, the CPS style is more general:

> makeSubType1' s = makeSubType2 WM s
> test3 = let foo x = case x of WM y -> method2 y
> 	in map (foo . makeSubType1') ["SubBase1", "SubBase2"]

More information about the Haskell-Cafe mailing list