<div dir="ltr"><br><br><div class="gmail_quote">On Wed, Aug 6, 2008 at 11:09 AM, Andrew Coppin <span dir="ltr">&lt;<a href="mailto:andrewcoppin@btinternet.com">andrewcoppin@btinternet.com</a>&gt;</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>
&nbsp;data Foo x = Foo Int Int<br>
<br>
Now &quot;Foo Int&quot; and &quot;Foo Double&quot; 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&#39;m working on.</blockquote>
<div><br>Phantom types are indeed useful for many things, but a bit of cautionary advice.&nbsp; 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.&nbsp; Bonus points if you can demonstrate an equivalent of unsafeCoerce# this way.<br>
<br>Example:<br>fooCast :: Foo Int -&gt; Foo Double<br>fooCast (Foo x) = Foo x<br><br>On noes!&nbsp; We just cast that Foo Int to a Foo Double without changing it!&nbsp; 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 -&gt; Foo Double<br>foo f@(Foo x) = f<br><br>Or, for that matter, any other variant where you don&#39;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 -&gt; Foo Int<br>mkFooInt = Foo<br><br>mkFooDouble :: Int -&gt; Foo Double<br>mkFooDouble = Foo<br><br>Now export these instead of your data constructor.&nbsp; Similarly you&#39;ll probably want ones to take the place of pattern matching.&nbsp; The most obvious general purpose one being:<br>
unFoo :: Foo a -&gt; Int<br>unFoo (Foo a) = a<br><br>But, if you&#39;re really relying on that phantom as a &quot;witness&quot;, then you should probably define separate unFooInt :: Foo Int -&gt; Int and unFooDouble : Foo Double -&gt; Int.<br>
<br>Here is the part where it gets really interesting.&nbsp; If you use GADTs, it rolls some of the above into one nice declaration:<br><br>data Foo a where<br>&nbsp; FooInt :: Int -&gt; Foo Int<br>&nbsp; FooDouble :: Int -&gt; Foo Double<br>
<br>I&#39;m not sure if the GADT way can lead to an unsafeCoerce# equivalents or not.&nbsp; Maybe someone else can comment on that.<br><br>Jason<br></div></div><br></div>