Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing an element of variable type in Haskell

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.

like image 640
Will Avatar asked Feb 21 '23 02:02

Will


2 Answers

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.

like image 68
Heatsink Avatar answered Mar 06 '23 08:03

Heatsink


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.

like image 21
Don Stewart Avatar answered Mar 06 '23 10:03

Don Stewart