Polymorphic return type, interfaces, callbacks?

haskell

Solution

There are two ways of solving this problem that I consider idiomatic Haskell:

Algebraic data type

data Goo = Hoo | Yoo

ham Hoo = "Hoo!"
ham Yoo = "Yoo!"

mustard Hoo = "Oh oh."
mustard Yoo = "Whew"

Pro: easy to add new operations Con: adding a new "type" potentially requires modifying many existing functions

Record of supported operations

data Goo = Goo { ham :: String, mustard :: String }

hoo = Goo { ham = "Hoo!", mustard = "Oh oh." }
yoo = Goo { ham = "Yoo!", mustard = "Whew" }

Pro: easy to add new "types" Con: adding a new operation potentially requires modifying many existing functions

You can of course mix and match these. Once you get used to thinking about functions, data and composition rather than interfaces, implementations and inheritance, these are good enough in a majority of cases.

Type classes are designed for overloading. Using them to mimic object-oriented programming in Haskell is usually a mistake.

Problem

Let's say `Goo` is my type class, which is often claimed to be the interface equivalent in languages like C++, Java or C#: ``` class Goo goo where ham :: goo -> String data Hoo = Hoo instance Goo Hoo where ham _ = "Hoo!" mustard _ = "Oh oh." data Yoo = Yoo instance Goo Yoo where ham _ = "Yoo!" mustard _ = "Whew" ``` But I cannot return a `Goo`: ``` paak :: (Goo goo) => String -> goo paak g = (Yoo) -- Could not deduce (goo ~ Yoo) -- from the context (Goo goo) -- bound by the type signature for paak :: Goo goo => String -> goo -- at frob.hs:13:1-14 -- `goo' is a rigid type variable bound by -- the type signature for paak :: Goo goo => String -> goo -- at frob.hs:13:1 -- In the expression: (Yoo) -- In an equation for `paak': paak g = (Yoo) ``` I found this enlightening statement, which explains why: The type `paak :: (Goo goo) => String -> goo` does not mean that the function might return any `Goo` it wants. It means that the function will return whichever `Goo` the user wants. (transliterated from sepp2k's answer here) But then, how could I return or store something that satisfies the `Goo` constraints, but can be `Hoo`, `Yoo`, `Moo`, `Boo` or any other `Goo`? Am I just entangled too much in own programming background, and need to think completely different, like resorting to C-like interfaces: ``` data WhewIamAGoo = WhewIamAGoo { ham' :: String mustard' :: String } paak :: String -> WhewIamAGoo paak g = let yoo = Yoo in WhewIamAGoo { ham' = ham yoo mustard' = mustard ham } ``` But that seems awkward. In my specific case, I would like to use `Goo` like this: ``` let x = someGoo .... in ham x ++ mustard x ``` I.e. the caller should not need to know about all the `Yoo`s and whatnot. edit: To clarify: I am looking for the way a Haskell programmer would go in such situation. How would you handle it in an idiomatic way?

Original source

Related problems