gliptak
gliptak

Reputation: 3670

transforming between ASTs in Haskell

There are lots of Haskell tutorials on parsing sources into an AST, AST rewrites, AST evaluations but not on mapping between two ASTs. Is there a "canonical" way to implement transformFoo2Bar in Haskell?

type FooIdentifier = String
data Foo = Foo FooIdentifier [FooMethod] deriving (Show)
data FooMethod = FooMethod FooIdentifier [FooExpression] deriving (Show)
data FooExpression = FooAB FooIdentifier FooIdentifier | FooZ FooIdentifier deriving (Show)

type BarIdentifier = String
type BarLabel = String
data Bar = Bar BarIdentifier [BarExpression] deriving (Show)
data BarExpression = BarA BarIdentifier BarIdentifier 
                   | BarB BarIdentifier BarIdentifier | BarZ BarIdentifier deriving (Show)

--
-- transformFoo2Bar [Foo "foo" [FooMethod "foo1" [FooAB "a" "b", FooZ "z"], FooMethod "foo2" [FooZ "z"]]]
--         
-- to evaluate to 
--
-- [Bar "foo_foo1" [BarA "a" "b", BarB "a" "b", BarZ "z"], Bar "foo_foo2" [BarZ "z"]] 
--

transformFoo2Bar :: [Foo] -> [Bar]
transformFoo2Bar = undefined

Similar to code compilation, but instead of emitting compiled code, keep the result as AST.

Also could some of these mappings be reused as reverse mapping?

Thanks

Upvotes: 1

Views: 318

Answers (1)

yatima2975
yatima2975

Reputation: 6610

The highly uncanonical way to do this is to change the definition of BarExpression around to

data BarExpression = BarB BarIdentifier | BarA BarIdentifier BarIdentifier deriving (Show)

so that it's not only structurally similar to FooExpression, but also has the same runtime representation.

Then you can use Unsafe.Coerce.unsafeCoerce:

fe1 = FooA "a"
fe2 = FooB "x" "y"

fm1= FooMethod "meth1" [fe1,fe2]

foo1 = Foo "foo" [fm1]

Which allows you to do

ghci> unsafeCoerce foo1 :: Bar
Bar "foo" [BarProc "meth1" [BarB "a",BarA "x" "y"]]

<font size+=1000 color="red"><blink>

Caveat programmator:

</blink></font>

I don't recommend doing this at all as it will put your eternal soul in peril, will break unexpectedly as soon as you look at it and in very, very bad ways (if you don't swap BarExpression this example starts causing segfaults :-) but it works for this particular use case.

Alternatively, you could get rid of one of the isomorphic types; is there a reason why you can't do this?

Upvotes: 1

Related Questions