CifarettoWiggum
CifarettoWiggum

Reputation: 137

Determine type of serialised data type in Haskell

I am writing two programs in Haskell, readFoo.hs and writeFoo.hs.

Foo is a datatype defined like this:

data Structure = ListStruct | TreeStruct

data Foo t = Foo {
    struct  :: Structure
  , content :: t Int
}

where t is either a list or a tree.

writeFoo.hs writes a value of type Foo t, where t gets determined by an option:

main = case option of
    ListStruct -> writeListFoo
    TreeStruct -> writeTreeFoo

Now i want readFoo.hs to read the written file and run a function depending on the structure of Foo:

main = do
    foo <- readFoo
    case struct foo of
        ListStruct -> runListFoo
        TreeStruct -> runTreeFoo

runListFunction :: Foo [] -> IO ()
runTreeFunction :: Foo Tree -> IO ()

but this fails, because obviously foo can't be of type Foo [] and Foo Tree at the same time.

Is there a way to do what i want?

Upvotes: 0

Views: 66

Answers (1)

user2407038
user2407038

Reputation: 14598

It would appear that you have a mistake in your formulation. The type Foo you've defined allows you write terms whose "tag" doesn't match the intended type of data:

-- Claims to be a "ListStruct" but the type is actually completely unrelated
badVal1 :: Foo Maybe
badVal1 = Foo ListStruct Nothing

-- Claims to be a "TreeStruct" but the type is actually list
badVal2 :: Foo []
badVal2 = Foo TreeStruct []

You can define your type as follows to disallow the above bad values:

data Structure x where
  ListStruct :: Structure []
  TreeStruct :: Structure Tree

data Foo s where
  Foo :: Structure s -> s Int -> Foo s

Note that with this change, Structure explicitly encodes the type of data which it requires.

If you have a Foo, but don't know of which type, you can express that as well:

data SomeFoo where
  SomeFoo :: Foo s -> SomeFoo

and you can operate on such values by pattern matching on the Structure argument, which allows you to discover the type of the data:

readFooAndDoSomething :: IO ()
readFooAndDoSomething = do
    SomeFoo (Foo struct datum) <- readFoo
    case struct of
        ListStruct -> runListFunction datum
        TreeStruct -> runTreeFunction datum


-- Left to the reader...
readFoo :: IO SomeFoo
runListFunction :: [Int] -> IO ()
runTreeFunction :: Tree Int -> IO ()

Upvotes: 2

Related Questions