I've finally managed to rig my Haskell program so that, at the very end, if I write
let foo = bar :: A
then I get one behaviour, and if I write
let foo = bar :: B
then I get the other desired behaviour.
Now I'd like my program to be able to parse this argument at runtime, but I really have no idea how to proceed. Any advice?
Edit: I'd like to parse some kind of (text) configuration file for which I am free to make up the specification/format.
One possible toy example is reading an integer as either an Int or a Double depending on further context provided in the configuration file, something along the lines of the following in the configuration file
barType: Int
barValue: 2
giving me bar = 2 :: Int, and
barType: Double
barValue: 2
giving me bar = 2 :: Double. Here it could be the case that I should be able to accept any type which has a Num instance.
In my case, I have a type class with some methods, and I'd like to parse anything with an instance for that type class; the methods could do something significantly different depending on the exact type. I don't know how I'd go about writing a Read instance for that.
Thanks.
If you are doing this, I presume you have some code whose contents depend on the type of foo, but whose return type does not. Suppose your full code example is
let foo = bar :: Int
in print (foo + 1)
Run-time parameter passing is normally accomplished by functions, so first rewrite the code as a function. The (Show a, Num a) =>
in the type signature shows that some type class methods are passed as a hidden parameter to the function.
printBar :: (Show a, Num a) => a -> IO ()
printBar x = print ((bar `asTypeOf` x) + 1)
For instance, printBar (undefined :: Double)
will use type Double
in its body. The compiler finds that the argument has type Double
, and it passes in the type class methods for Double
.
Now, let's write a function that chooses the actual type of printBar
.
invokeBar :: Bool -> (forall a. (Show a, Num a) => a -> b) -> b
invokeBar True f = f (undefined :: Int)
invokeBar False f = f (undefined :: Double)
Now you can call invokeBar True printBar
or invokeBar False printBar
to choose whether to use Int
or Double
.
You cannot read in an "unknown type" from a file, because there is no file format encompassing all possible types. However, you can make a parser that recognizes the names of the types you will use, and use the approach above for each of those names.
If you're looking for different behaviors based on the type being instantiated, then you need a type class. Good examples to study would be Data.Binary instances of even the Read
type class.
E.g.
class Binary t where
-- | Decode a value in the Get monad
get :: Get t
You didn't say what kind of parsing you wanted to do -- text? binary? -- but it might be as simple as just reusing existing parsing libraries, and writing instances for your types.
Alternatively, you could use parser combinators to choose between different options.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With