5291


Haskell の レコード構文(Record Syntax)にて、簡潔なフィールド名を使い定義すると名前がかぶりやすくなる問題があります。

[参考サイト](http://book.realworldhaskell.org/read/defining-types-streamlining-
functions.html#deftypes.record)
を見ると、レコード名をprefixとしたフィールド名を使うとよいようですが、こんなものなのでしょうか？  
よりよい方法はないものでしょうか？

エラーになる例:

[code]

    data Java = Java { power :: Integer ; url :: String }
    data Link = Link { title :: String ; url :: String }
    -- 下記が定義されるためか、重複してしまう
    -- url :: Java -> String
    -- url :: Link -> String
    
[/code]

エラー内容:

> Multiple declarations of ‘url’  
>  Declared at: (省略)

* * *

改善した例:

[code]

    data Java = Java { javaPower :: Integer ; javaUrl :: String }
    data Link = Link { linkTitle :: String ; linkUrl :: String }
    
[/code]

* * *

※ 「命名規則や用語がHaskell的に変」という場合もぜひ、コメントや編集などでフィードバックお願いします


CC BY-SA 3.0
2015-01-26T00:38:25.553
5278
2015-02-27T21:10:03.807
5021
question
haskell
Haskell の レコード構文(Record Syntax)にて、簡潔なフィールド名を定義すると重複しやすい問題の解決方法


7.10現在、GHCでは重複したフィールド名をうまく扱うことができません。しかし、GHC
7.12からは、OverloadedRecordFields(<https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/Design>)と呼ばれる拡張が導入される予定で、それを使えば同名のフィールドを定義できます。

OverloadedRecordFieldsを利用すると、urlに以下のような型を持たせることができます。

[code]

    url :: r { url :: String } => r -> Integer
    
[/code]

`r { url :: String }`は、レコード`r`が`url ::
String`というフィールドを持っているという制約を示しており、フィールド名によるオーバーロードが可能になります。

また、現在のHaskell(GHC)でオーバーロードされたフィールドを実現する研究は多数存在し(<https://wiki.haskell.org/Extensible_record>)、盛んにライブラリも作られています。

最近リリースされたrecordというライブラリは、Template
Haskellを用いて、OverloadedRecordFieldsを使わずにそれに近い記述を可能にしています。作者のページ(<http://nikita-
volkov.github.io/record/>)で解説されていますが、インターフェイスは比較的シンプルで、パフォーマンス上のデメリットも少ないため、お勧めできます。recordを使うと、JavaとLinkの例はこのようになります。

[code]

    {-# LANGUAGE QuasiQuoters #-}
    import Record
    import Record.Lens
    
    type Java = [r| { power :: Integer, url :: String } |]
    type Link = [r| { title :: String, url :: String } |]
    
    example :: Link
    example = [r|{ title = "example", url = "http://www.example.org" }|]
    
    -- view [l|url] :: Java -> String
    -- view [l|url] :: Link -> String
    
[/code]

宣伝になってしまいますが、私もこの問題に取り組んでいて、[extensible](http://hackage.haskell.org/package/extensible)というライブラリを開発しています。こちらは、まずフィールド名と型をあらかじめ決めるというアプローチをとっているので、ほかの言語における構造体とは毛色が異なります。

[code]

    {-# LANGUAGE TemplateHaskell, DataKinds, TypeOperators, TypeFamilies, FlexibleContexts #-}
    import Data.Extensible.Record
    import Data.Extensible.Record.Internal.Rig
    
    mkField "power" [t|Integer|]
    mkField "url" [t|String|]
    mkField "title" [t|String|]
    
    type Java = Record '["power", "url"]
    type Link = Record '["title", "url"]
    
    example :: Link
    example = title @= "example" 
           <: url @= "www.example.org"
           <: Nil
    -- view url :: "url" ∈ xs => Record xs -> String
    
[/code]

OverloadedRecordFieldsはフィールドが第一級でない、recordはTemplate
Haskellが必要、extensibleを含む拡張可能レコードはパフォーマンスがあまり良くないというデメリットがあり、Haskellerの間でも共通の答えは出ていないのが現状です。


CC BY-SA 3.0
2015-01-26T02:28:33.300
5291
2015-01-26T06:10:26.057
2015-01-26T06:10:26.057
2826
2826
5278
answer


私が知っている、もっとも簡単な対処法はLensを利用することです。

`lens`ライブラリを使う場合、もっとも簡単に書けば以下のようになります。

[code]

    {-# LANGUAGE TemplateHaskell #-}
    import Control.Lens
    
    data Java  =  Java  { javaPower ::  Integer  ; javaUrl ::  String  } 
    data Link  =  Link  { linkTitle ::  String   ; linkUrl ::  String  } 
    
    makeFields ''Java
    makeFields ''Link
    
[/code]

すると、両方に対して使える`url`コンビネータが"lens"として定義され、`power`と`title`もlensとしてそれぞれの型に対し定義されます。

"^."、".~"、"&"演算子で、これらのLensを使ってレコードを操作することができます。

[code]

    ghci> Java 12 "http://..." ^. url
    "http://..."
    
    ghci> Java 12 "http://..." & power .~ 9000
    Java 9000 "http://..."
    
    ghci> Link "start" "http://..." ^. url
    "http://..."
    
    ghci> Link "start" "http://..." & title .~ "end"
    Link "end" "http://..."
    
[/code]

`lens`のチュートリアルや発表は、私のも含めインターネット上に多数存在します。

そのほとんどは英語ですが、少なくとも1つは、[`its_out_of_tune`](https://twitter.com/its_out_of_tune)さんが以前の
_勉強会_ で発表した日本語のものがあります。

ちゅーんさんのスライドは今でも閲覧できます。

<http://www.slideshare.net/itsoutoftunethismymusic/ekmett-17955009>

残念ながら、発表は録画されていたのですが、もうUstreamには残っていないようです。(▰˘︹˘▰)


CC BY-SA 3.0
2015-02-08T18:34:37.013
6161
2015-02-27T21:10:03.807
2015-02-27T21:10:03.807
8101
8101
5278
answer