[Haskell-cafe] Ambiguous type variable - help!
gale at sefer.org
Sun Jul 19 16:18:32 EDT 2009
> I'm trying to work out how to handle a choice at runtime
> which determines what instance of a State monad should
> be used.
First of all, you should realize that you'll almost never want
to do something like that in Haskell.
In my opinion, if you're coming from an OO language, you
should ban yourself from defining Haskell classes or using
existential types until you are completely comfortable with
how different Haskell is from OO. You can get along fine
> I've concocted a very simple example to illustrate this (below) - but
> it doesn't compile because ghc complains that my type is ambiguous arising
> from my use of 'fromSeq'.
Notice that you have given two completely separate sets
of instructions of what to do depending on whether Int
or Double is selected. You have not given any indication
of how to choose between them, even at runtime. Of course,
the compiler doesn't care that your string constants "Int" and
"Double" happen also to be the names of types if unquoted.
The way you avoid boilerplate in Haskell in these kinds of
cases is by using polymorphism. Note that there could still
remain a small amount of boilerplate - you move the actual
hard work into a single polymorphic function, but then you
may still need to mention that function once for each type.
If that bothers you, there are more advanced tools to get
rid of that last bit of boilerplate, like Template Haskell or
"Scrap Your Boilerplate".
Below is one way to fix up your example, with a few other minor
bits of polish.
import Control.Monad.State -- Why Strict? Haskell is lazy by default.
data SeqType = SeqInt Int | SeqDouble Double
class SequenceClass a where
nextSeq :: State a Int
instance SequenceClass Int where
nextSeq = State $ \s -> (s, s + 1)
instance SequenceClass Double where
nextSeq = State $ \s -> (truncate s, s + 1)
chooser :: String -> SeqType
chooser inStr | inStr == "Double" = SeqDouble 1
| otherwise = SeqInt 1
-- Here is the polymorphism.
-- Make this a function so that we can move it
-- out of main.
result :: SequenceClass a => a -> [Int]
result = evalState $ replicateM 10 nextSeq
-- Here is the only boilerplate needed
printResult :: SeqType -> IO ()
printResult (SeqInt i) = print $ result i
printResult (SeqDouble x) = print $ result x
main :: IO()
main = do userInput <- getLine
printResult $ chooser userInput
-- or you could just say
-- main = getLine >>= printResult . chooser
More information about the Haskell-Cafe