<div dir="ltr"><br><br><div class="gmail_quote">On Wed, Aug 6, 2008 at 11:09 AM, Andrew Coppin <span dir="ltr"><<a href="mailto:andrewcoppin@btinternet.com">andrewcoppin@btinternet.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
I just (re)discovered that I can do things like<br>
<br>
data Foo x = Foo Int Int<br>
<br>
Now "Foo Int" and "Foo Double" are, as far as the type checker cares, two completely different types, even though in fact they are the same. This is actually Quite Useful, in the particular case I'm working on.</blockquote>
<div><br>Phantom types are indeed useful for many things, but a bit of cautionary advice. If you start to depend on the phantoms for type safety AND you export your data constructors then you run a serious risk of being type unsafe. Bonus points if you can demonstrate an equivalent of unsafeCoerce# this way.<br>
<br>Example:<br>fooCast :: Foo Int -> Foo Double<br>fooCast (Foo x) = Foo x<br><br>On noes! We just cast that Foo Int to a Foo Double without changing it! It works because the value on the RHS is consider freshly constructed and other than sharing x it is unrelated to the one on the LHS.<br>
<br>Note that by contrast this does not type check:<br>foo :: Foo Int -> Foo Double<br>foo f@(Foo x) = f<br><br>Or, for that matter, any other variant where you don't use the constructor on the RHS.<br><br>One way to get around this is to create your own constructor functions:<br>
<br>mkFooInt :: Int -> Foo Int<br>mkFooInt = Foo<br><br>mkFooDouble :: Int -> Foo Double<br>mkFooDouble = Foo<br><br>Now export these instead of your data constructor. Similarly you'll probably want ones to take the place of pattern matching. The most obvious general purpose one being:<br>
unFoo :: Foo a -> Int<br>unFoo (Foo a) = a<br><br>But, if you're really relying on that phantom as a "witness", then you should probably define separate unFooInt :: Foo Int -> Int and unFooDouble : Foo Double -> Int.<br>
<br>Here is the part where it gets really interesting. If you use GADTs, it rolls some of the above into one nice declaration:<br><br>data Foo a where<br> FooInt :: Int -> Foo Int<br> FooDouble :: Int -> Foo Double<br>
<br>I'm not sure if the GADT way can lead to an unsafeCoerce# equivalents or not. Maybe someone else can comment on that.<br><br>Jason<br></div></div><br></div>