Creating different types from an unknown sequence, in Haskell?

Viewed 117

Assuming a function that "gets" a sequence of information (e.g. a list of characters), and shall create different types from it, where its type depends on the input sequence, which represents the content - and assuming the types are already given.

numFromString :: [Char] -> ???

I suppose there is several possibilities.

My first idea is to use a type parameter.

main :: IO ()
main =
    do
        sLine <- getLine
        print $ numFromString sLine

numFromString :: (Show a, Read a) => String -> (Maybe a)
numFromString ('I':'n':'t':'e':'g':'e':'r':rs) = Just ((read rs) :: Integer)
--                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this does not work
numFromString ('I':'n':'t':rs) = Just ((read rs) :: Int)
numFromString _ = Nothing

...but we cannot just provide an Integer where a type variable is expected or can we?.

My second idea is to use type class

main :: IO ()
main = 
    do
        sLine <- getLine
        print $ ((numFromString sLine) :: (Maybe Integer))
        --                                ^^^^^^^^^^^^^^^ I have to decide in advance which type I want to get

class (Show a, Read a) => XClass a where
    numFromString :: String -> (Maybe a)

instance XClass Int where
    numFromString ('I':'n':'t':rs) = Just ((read rs) :: Int)
    numFromString _ = Nothing

instance XClass Integer where
    numFromString ('I':'n':'t':'e':'g':'e':'r':rs) = Just ((read rs) :: Integer)
    numFromString _ = Nothing

...but this doesnt work as well, when we use numFromString, do we?.

My third idea is to use a sum kind of data type.

main :: IO ()
main =
    do
        sLine <- getLine
        print $ numFromString sLine

data X = XInt Int | XInteger Integer | XNone
    deriving Show

numFromString :: String -> X
numFromString ('I':'n':'t':'e':'g':'e':'r':rs) = XInteger (read rs)
numFromString ('I':'n':'t':rs) = XInt (read rs)
numFromString _ = XNone

Is there a more elegant way?

Would generic programming help?

How would it look like?

1 Answers

Your sum type approach looks like the right way to me.

For examples of this in the wild:

  • The first character of a JSON document determines a type -- { for objects, [ for lists, " for strings, etc. aeson, a popular Haskell JSON parser, uses a sum type that can represent each of these.
  • An almost identical situation is involved in parsing CBOR. cborg uses a sum type to capture all the possibilities.
  • The Smart Games Format can represent the record of moves from a variety of games. The kind of information needed to store a move varies from game to game; the sgf library uses a sum type to capture the possibilities.

...and so on.

Related