The issue here is the meaning of -> a
in your function type. If you're function actually had this type, then whoever called your function should be able to specify a concrete type of their choosing (that you may not even have in scope) and then expect your function to work as if it had the type
String -> String -> MyCustomType
However this clearly isn't what you had in mind. You don't mean "for all types a
, I have a function ...", you mean "For any two strings, there is some type a
for which I have a value". This idea, that you get to choose the type variable instead of the caller, is called "existential quantification" and GHC does support it. However I don't really think that's what you want to do. After all, when you go to actually use this function, you'll probably want to be able to case on whether or not you got back a UTCTime
or a Double
or something. Since you cannot do this with existential quantification (just like how you cannot case on type variables in polymoprhic functions) we should instead create a custom data type:
data Dyn = String String | Int Int | BigInt Integer | UTCDate UTCTime ...
and so on. That is, you list out an explicit constructor for each case that your type may return and then your function will read
toProperType :: String -> String -> Dyn
toProperType tp val =
case tp of
"string" -> String val -- ::String
"int" -> Int $ toIntType val -- ::Int64
"bigint" -> BigInt $ toIntType val -- ::Int64
"integer"-> Integer $ toIntType val
"utcdate"-> UTCDate $ toDateType val -- :: UTCTime
"double" -> Double $ toDoubleType val -- :: Double
This is how serious Haskell libraries handle things like JSON parsing or what not so you're in good company. Now it's well typed and whoever calls this function just cases on the Dyn
value and decides what to do based on the returned type.