[prev in list] [next in list] [prev in thread] [next in thread] 

List:       haskell-cafe
Subject:    Re: [Haskell-cafe] Record syntax, reopening a can of worms.
From:       Yves_Parès <limestrael () gmail ! com>
Date:       2012-05-27 9:55:50
Message-ID: CACqaG6w+eVda_RO_1jTZEkVhx3SUnQzJTwVsm7iAFFVZZi4Xkg () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


I enclosed a source file that shows the use of a GADT in that case.

2012/5/27 <timothyhobbs@seznam.cz>

> Somehow I don't understand you.
> Could you please fill out your example into a working bit of code?
>
> Thank you,
>
> Timothy
>
>
> ---------- Původní zpráva ----------
> Od: Yves Parès <limestrael@gmail.com>
>
> Datum: 27. 5. 2012
> Předmět: Re: [Haskell-cafe] Record syntax, reopening a can of worms.
>
> > case myData of
> >   myA@A{} -> fooForA's myA
> >   myB@B{} -> fooForB's myB
>
> I think this would typecheck if you used GADTs.
> Actually what you'd want is to use the record syntax with GADTs (there are
> to add the extra type safety you want), however both are not compatible.
>
> data ALike
> data BLike
>
> data MyData x where
>     A :: Int -> Int -> MyData ALike
>     B :: Int -> MyData BLike
>
> a (A x _) = x
> b (A _ x) = x
> c (B x) = x
> -- GHC may require you to write the type signatures for those three
> functions.
> Le 27 mai 2012 10:52, <timothyhobbs@seznam.cz> a écrit :
>
> Your Maybe example is very contrived.  The place where I ran into this was
> much less contrived I think.
>
> I have an editor for a visual programming language. That looks like this:
>
> https://github.com/timthelion/gridhaskell-haskarrow/wiki
>
> I'm using a modified version of the Document-View model for application
> design.  The commands in the language are defined in the Cell data type:
>
>
> https://github.com/timthelion/gridhaskell-haskarrow/blob/master/Cell.lhs
>
>
> Some Cell types, like Jump, have a long crooked line comming out of them
> called a Path.  This line is held in the path :: Maybe Path feild.
>
>
> When I'm drawing the Cell tree, I have a function that extracts these
> Paths.
> https://github.com/timthelion/gridhaskell-haskarrow/blob/master/ArrowDrawing.lhs#L75
>
>
> It used to be, that more types of Cell than just the Jump Cell had a path.
>
>
> As I removed these Paths from the Cell datatype, my line drawing function
> started crashing, whenever it encountered those Cell types.  By having
> chosen to use the short hand record selector syntax rather than the long
> hand place value syntax, I caused a runtime error in my code.
>
>
> I could of course have written the same function like this:
>
> >   where path_points =
>
>
> >          case
> >           case cell of
>
>
>
> Types of cells which have paths:
>
> >             Cell.Jump _ path        -> Just path
>
>
> >             _                  -> Nothing of
> >          Just path -> maybe [] (\p -> [Path.point p]) (Cell.path cell)
>
>
> >          Nothing   -> []
>
>
> Record selection is a more convenient syntax.  It *could* be usefull for
> making more maintainable code(the reason why I choose it).  The method I
> have chosen *would* be more maintainable in the case I want to add another
> feild to Jump.  In that case I would not have to re-write the path_points
> function.  Sadly, due to GHC's unnessesary and complete inability to do
> type safety checking, what should have been a convenient tool, has become
> an unnessecary source of runtime errors!
>
>
> How would I use your syntax in functions that want to pattern match
> against both A and B?  I tried this without success:
>
> >data ALike
> >data BLike
>
> >data MyData t = A {a::Int,
> > b::Int} |
> > B {c::Int}
>
> >mkA x y = A x y :: MyData ALike
> >mkB x = B x :: MyData BLike
>
> altRecordSyntaxes.lhs:15:18:
>     Couldn't match type `BLike' with `ALike'
>     Expected type: MyData ALike
>       Actual type: MyData t
>     In the first argument of `g', namely `myA'
>     In the expression: g myA
>
> >foo :: MyData t -> Int
>
> >foo myA@A{} = g myA
>
> > where
>
> >  g :: MyData ALike -> Int
>
> >  g myA' = a myA'
>
> >foo myB@B{} = g myB
>
> > where
>
> >  g :: MyData BLike -> Int
>
> >  g myB' = c myB'
>
> >main :: IO ()
>
> >main = do
>
> >  print $ foo $ mkA 1 3
>
>
> This also doesn't work:
>
> >foo :: MyData t -> Int
>
> >foo myData =
>
> > case myData of
>
> >  myA@A{} -> fooForA's myA
>
> >  myB@B{} -> fooForB's myB
>
> > where
>
> >  fooForA's :: MyData ALike -> Int
>
> >  fooForA's myA' = a myA'
>
> >  fooForB's :: MyData BLike -> Int
>
> >  fooForB's myB' = a myB'
>
> >main :: IO ()
>
> >main = do
>
> >  print $ foo $ mkA 1 3
>
> My solution looks very clean(except for that nasty looking data
> declaration) and has the same advantages.  Really, the current record
> syntax is just flawed :D
>
> >data MyData = A A' | B B'
>
> >data A' = A'{a::Int,
> >             b::Int}
> >data B' = B'{c::Int}
>
> >foo :: MyData -> Int
> >foo (A myA) = a myA
> >foo (B myB) = c myB
>
> >main :: IO ()
> >main = print $ foo (A (A' 1 2))
>
> Timothy
>
> ---------- Původní zpráva ----------
> Od: John Meacham <john@repetae.net>
> Datum: 27. 5. 2012
> Předmět: Re: [Haskell-cafe] Record syntax, reopening a can of worms.
>
> Is it any more ridiculous than
>
> > f x@Nothing {} = fromJust x
> > main = print (f Nothing)
>
> crashing at run time? That is what you are expressing with your first
> one. This issue is completely unrelated to the named field syntax,
> they behave exactly like data types with non-named fields.
>
> However, you can achieve something like what you want with phantom types.
>
> > data ALike
> > data BLike
>
> >data MyData t = A {a::Int,
> > b::Int} |
> > B {c::Int}
>
> > mkA x y = A x y :: MyData ALike
> > mkB x = B x :: MyData BLike
>
> then you can write functions of
> 'MyData ALike' to indicate it will only have 'A' as a constructor
> 'MyData BLike' to indicate it will only have 'B'
> and 'forall t . MyData t' for functions that can take a general MyData
> that can have either.
>
> John
>
>
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe@haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>
>

[Attachment #5 (text/html)]

I enclosed a source file that shows the use of a GADT in that case.<br><br><div \
class="gmail_quote">2012/5/27  <span dir="ltr">&lt;<a \
href="mailto:timothyhobbs@seznam.cz" \
target="_blank">timothyhobbs@seznam.cz</a>&gt;</span><br>

<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"> <div>Somehow I don&#39;t understand you.<br>Could you please \
fill out your example into a working bit of code?<br><br>Thank \
you,<br><p>Timothy</p><p><br></p><p>---------- Původní zpráva ----------<br>Od: \
Yves Parès &lt;<a href="mailto:limestrael@gmail.com" \
target="_blank">limestrael@gmail.com</a>&gt;</p>


<div><div><br>Datum: 27. 5. 2012<br>Předmět: Re: [Haskell-cafe] Record syntax, \
reopening a can of worms.</div></div><p></p><div><div><blockquote><div><p>&gt; case \
myData of<br> &gt;     myA@A{} -&gt; fooForA&#39;s myA<br>
&gt;     myB@B{} -&gt; fooForB&#39;s myB</p>
<p>I think this would typecheck if you used GADTs.<br>
Actually what you&#39;d want is to use the record syntax with GADTs (there are to add \
the extra type safety you want), however both are not compatible.</p> <p>data \
ALike<br> data BLike</p>
<p>data MyData x where<br>
       A :: Int -&gt; Int -&gt; MyData ALike<br>
       B :: Int -&gt; MyData BLike</p>
<p>a (A x _) = x<br>
b (A _ x) = x<br>
c (B x) = x<br>
-- GHC may require you to write the type signatures for those three functions.</p>
<div>Le 27 mai 2012 10:52, &lt;<a href="mailto:timothyhobbs@seznam.cz" \
target="_blank">timothyhobbs@seznam.cz</a>&gt; a écrit  :<br><blockquote \
style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> <div>Your \
Maybe example is very contrived.   The place where I ran into this was much less \
contrived I think.<br><br>I have an editor for a visual programming language. That \
looks like this:<br><br><a \
href="https://github.com/timthelion/gridhaskell-haskarrow/wiki" \
target="_blank">https://github.com/timthelion/gridhaskell-haskarrow/wiki</a><br>



<br><p>I&#39;m using a modified version of the Document-View model for application \
design.   The commands in the language are defined in the Cell data \
type:</p><p><br></p><p><a \
href="https://github.com/timthelion/gridhaskell-haskarrow/blob/master/Cell.lhs" \
target="_blank">https://github.com/timthelion/gridhaskell-haskarrow/blob/master/Cell.lhs</a><br>




</p><p><br></p><p>Some Cell types, like Jump, have a long crooked line comming out of \
them called a Path.   This line is held in the path :: Maybe Path \
feild.</p><p><br></p><p> When I&#39;m drawing the Cell tree, I have a function that \
extracts these Paths.   <a \
href="https://github.com/timthelion/gridhaskell-haskarrow/blob/master/ArrowDrawing.lhs#L75" \
target="_blank">https://github.com/timthelion/gridhaskell-haskarrow/blob/master/ArrowDrawing.lhs#L75</a></p>




<p><br></p><p>It used to be, that more types of Cell than just the Jump Cell had a \
path.</p><p><br></p><p>As I removed these Paths from the Cell datatype, my line \
drawing function started crashing, whenever it encountered those Cell types.   By \
having chosen to use the short hand record selector syntax rather than the long hand \
place value syntax, I caused a runtime error in my code.<br>



</p><p><br></p><p>I could of course have written the same function like \
this:<br></p><pre><div style="background-color:transparent"><span>&gt;   \
</span><span>where</span> <span>path_points</span> <span>=</span> </div><div \
style="background-color:transparent">



<span>&gt;          </span><span>case</span></div><div \
style="background-color:transparent"><span>&gt;           </span><span>case</span> \
<span>cell</span> <span>of</span></div><div style="background-color:transparent"><br>



</div><div style="background-color:transparent">Types of cells which have \
paths:</div><div style="background-color:transparent"><br></div><div \
style="background-color:rgb(255,255,204)"><span>&gt;             \
</span><span>Cell</span><span>.</span><span>Jump</span><span> _ path</span>        \
<span>-&gt;</span> Just path<span></span></div>



<div style="background-color:transparent"><span>&gt;             \
</span><span>_</span>                  <span>-&gt;</span> <span>Nothing \
of</span></div><div style="background-color:transparent"><span>&gt;          \
</span><span>Just path -&gt;</span> <span>maybe</span> <span>[]</span> \
<span>(</span><span>\</span><span>p</span> <span>-&gt;</span> \
<span>[</span><span>Path</span><span>.</span><span>point</span> \
<span>p</span><span>])</span> \
<span>(</span><span>Cell</span><span>.</span><span>path</span> \
<span>cell</span><span>)</span></div>



<div style="background-color:transparent"><span>&gt;          </span><span>Nothing   \
-&gt;</span> <span>[]</span></div></pre><p><br></p><p>Record selection is a more \
convenient syntax.   It *could* be usefull for making more maintainable code(the \
reason why I choose it).   The method I have chosen *would* be more maintainable in \
the case I want to add another feild to Jump.   In that case I would not have to \
re-write the path_points function.   Sadly, due to GHC&#39;s unnessesary and complete \
inability to do type safety checking, what should have been a convenient tool, has \
become an unnessecary source of runtime errors!<br>



</p><p><br></p>How would I use your syntax in functions that want to pattern match \
against both A and B?   I tried this without success:<br><br>&gt;data \
ALike<br>&gt;data BLike<br><br>&gt;data MyData t = A {a::Int,<br>&gt; b::Int} |<br>



&gt; B {c::Int}<br><br>&gt;mkA x y = A x y :: MyData ALike<br>&gt;mkB x = B x :: \
MyData BLike<br><br>altRecordSyntaxes.lhs:15:18:<br>       Couldn&#39;t match type \
`BLike&#39; with `ALike&#39;<br>       Expected type: MyData ALike<br>



           Actual type: MyData t<br>       In the first argument of `g&#39;, namely \
`myA&#39;<br>       In the expression: g myA<br><br>&gt;foo :: MyData t -&gt; \
Int<br><br>&gt;foo myA@A{} = g myA<br><br>&gt; where<br><br>&gt;   g :: MyData ALike \
-&gt; Int<br>



<br>&gt;   g myA&#39; = a myA&#39;<br><br>&gt;foo myB@B{} = g myB<br><br>&gt; \
where<br><br>&gt;   g :: MyData BLike -&gt; Int<br><br>&gt;   g myB&#39; = c \
myB&#39;<br><br>&gt;main :: IO ()<br><br>&gt;main = do<br><br>&gt;   print $ foo $ \
mkA 1 3<br>



<br><br>This also doesn&#39;t work:<br><br>&gt;foo :: MyData t -&gt; \
Int<br><br>&gt;foo myData =<br><br>&gt; case myData of<br><br>&gt;   myA@A{} -&gt; \
fooForA&#39;s myA<br><br>&gt;   myB@B{} -&gt; fooForB&#39;s myB<br><br>



&gt; where<br><br>&gt;   fooForA&#39;s :: MyData ALike -&gt; Int<br><br>&gt;   \
fooForA&#39;s myA&#39; = a myA&#39;<br><br>&gt;   fooForB&#39;s :: MyData BLike -&gt; \
Int<br><br>&gt;   fooForB&#39;s myB&#39; = a myB&#39;<br><br>



&gt;main :: IO ()<br><br>&gt;main = do<br><br>&gt;   print $ foo $ mkA 1 3<br><br>My \
solution looks very clean(except for that nasty looking data declaration) and has the \
same advantages.   Really, the current record syntax is just flawed :D<br>



<br>&gt;data MyData = A A&#39; | B B&#39;<br><br>&gt;data A&#39; = \
A&#39;{a::Int,<br>&gt;                         b::Int}<br>&gt;data B&#39; = \
B&#39;{c::Int}<br><br>&gt;foo :: MyData -&gt; Int<br>&gt;foo (A myA) = a \
myA<br>&gt;foo (B myB) = c myB<br>



<br>&gt;main :: IO ()<br>&gt;main = print $ foo (A (A&#39; 1 \
2))<br><br>Timothy<br><br><p>---------- Původní zpráva ----------<br>Od: John \
Meacham &lt;<a href="mailto:john@repetae.net" \
target="_blank">john@repetae.net</a>&gt;<br>



Datum: 27. 5. 2012<br>Předmět: Re: [Haskell-cafe] Record syntax, reopening a can of \
worms.</p><blockquote>Is it any more ridiculous than<br><br>&gt; f x@Nothing {} = \
fromJust x<br>&gt; main = print (f Nothing)<br><br>crashing at run time? That is what \
you are expressing with your first<br>



one. This issue is completely unrelated to the named field syntax,<br>they behave \
exactly like data types with non-named fields.<br><br>However, you can achieve \
something like what you want with phantom types.<br><br>&gt; data ALike<br>



&gt; data BLike<br><br>&gt;data MyData t = A {a::Int,<br>&gt; b::Int} |<br>&gt; B \
{c::Int}<br><br>&gt; mkA x y = A x y :: MyData ALike<br>&gt; mkB x = B x :: MyData \
BLike<br><br>then you can write functions of<br>&#39;MyData ALike&#39; to indicate it \
will only have &#39;A&#39; as a constructor<br>



&#39;MyData BLike&#39; to indicate it will only have &#39;B&#39;<br>and &#39;forall t \
. MyData t&#39; for functions that can take a general MyData<br>that can have \
either.<br><br> John</blockquote></div><br>_______________________________________________<br>





Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org" \
target="_blank">Haskell-Cafe@haskell.org</a><br> <a \
href="http://www.haskell.org/mailman/listinfo/haskell-cafe" \
target="_blank">http://www.haskell.org/mailman/listinfo/haskell-cafe</a><br> \
<br></blockquote></div> </div></blockquote></div></div></div></blockquote></div><br>

--bcaec52be76f235b2f04c1019c9e--


["TestGADTs.hs" (application/octet-stream)]

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic