[Haskell-cafe] Record syntax, reopening a can of worms.

timothyhobbs at seznam.cz timothyhobbs at seznam.cz
Sun May 27 11:24:43 CEST 2012


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 at gmail.com>
Datum: 27. 5. 2012
Předmět: Re: [Haskell-cafe] Record syntax, reopening a can of worms.
"

> case myData of
>   myA at A{} -> fooForA's myA
>   myB at 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 at seznam.cz(mailto:timothyhobbs at 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
(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
(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
(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:


<div style='background-color:transparent'><span>>   </span><span>where</span> <span>path_points</span> <span>=</span> </div><div style='background-color:transparent'>
<span>>          </span><span>case</span></div><div style='background-color:transparent'><span>>           </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>>             </span><span>Cell</span><span>.</span><span>Jump</span><span> _ path</span>        <span>-></span> Just path<span></span></div>
<div style='background-color:transparent'><span>>             </span><span>_</span>                  <span>-></span> <span>Nothing of</span></div><div style='background-color:transparent'><span>>          </span><span>Just path -></span> <span>maybe</span> <span>[]</span> <span>(</span><span>\</span><span>p</span> <span>-></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>>          </span><span>Nothing   -></span> <span>[]</span></div>




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 at A{} = g myA

> where

>  g :: MyData ALike -> Int

>  g myA' = a myA'

>foo myB at 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 at A{} -> fooForA's myA

>  myB at 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 at repetae.net(mailto:john at 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 at 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 at haskell.org(mailto:Haskell-Cafe at haskell.org)
http://www.haskell.org/mailman/listinfo/haskell-cafe
(http://www.haskell.org/mailman/listinfo/haskell-cafe)

"

"
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20120527/b6786866/attachment.htm>


More information about the Haskell-Cafe mailing list