Chang Peng
Chang Peng

Reputation: 1062

Pass Types as arguments to a function in Haskell?

The following two functions are extremely similar. They read from a [String] n elements, either [Int] or [Float]. How can I factor the common code out? I don't know of any mechanism in Haskell that supports passing types as arguments.

readInts n stream = foldl next ([], stream) [1..n]
  where
    next (lst, x:xs) _ = (lst ++ [v], xs)
      where
        v = read x :: Int

readFloats n stream = foldl next ([], stream) [1..n]
  where
    next (lst, x:xs) _ = (lst ++ [v], xs)
      where
        v = read x :: Float

I am at a beginner level of Haskell, so any comments on my code are welcome.

Upvotes: 9

Views: 5931

Answers (2)

Philip JF
Philip JF

Reputation: 28539

Haskell supports a high degree of polymorphism. In particular

readAny n stream = foldl next ([], stream) [1..n]
  where
    next (lst, x:xs) _ = (lst ++ [v], xs)
      where
        v = read x 

has type

readAny :: (Enum b, Num b, Read a) => b -> [String] -> ([a], [String])

thus

readInts :: (Enum b, Num b) => b -> [String] -> ([Int], [String])
readInts = readAny

readFloats :: (Enum b, Num b) => b -> [String] -> ([Float], [String])
readFloats = readAny

you dont need to specialize the type. Haskell will automatically infer the most general type possible, and the readAny here will do what you want.

It is not possible to pass types as arguments in Haskell. Rarely would you need to. For those few cases where it is necessary you can simulate the behavior by passing a value with the desired type.

Haskell has "return type polymorphism" so you really shouldn't worry about "passing the type"--odds are that functions will do what you want without you telling them to.

Upvotes: 16

plediii
plediii

Reputation: 451

Basically what you want is to not explicitly declare the type. Instead, defer declaring the type and let the inference engine take over for you. Also, I think you are conflating fold with map. This is how I would approach it.

readList' :: Read a => [String] -> [a]
readList' = map read


ints = readList' ["1", "2"] :: [Int] -- [1, 2]

floats = readList' ["1.0", "2.0"] :: [Float] -- [1.0, 2.0]

To read only n things from the stream, use take

Upvotes: 9

Related Questions