2016-07-07 5 views
3

は文字でフィールド名を置き換える、私はこのような例があります:大きなHaskellレコードを管理するより良い方法は何ですか?

私はより良いこの成長を管理することができるようになるどんな技術?

ab、およびcはすべて同じタイプなので、リストに入れておくことができますが、さまざまな種類があります。私は特に、大規模なパターンを必要とせずにレコードを折りたたむ方法に興味があります。

私はこの大きなレコードを使用して、permutation parsing the vCard formatの異なるタイプを保持しています。

更新

私はgenericsの両方を実装しましたし、foldlアプローチは、以下の提案。両方とも動作し、両方ともthree large field listsを1に減らします。

+0

レコードフィールドに名前を付ける必要がありますか( 'Foo'はレコード以外のものになる可能性があります)? – Alec

+1

このXYの臭いが問題です。たぶん、そのような「Foo」を全く持たないという解決策があるかもしれませんが、その判断をするには、「Foo」が解決しなければならない問題についての情報を与えなければなりません。 – Bakuriu

+1

おそらく "generics-sop"のようなジェネリックライブラリを使って、データ型ジェネリックプログラミングのユースケースのようです。 – danidiaz

答えて

2

ヒント:

(1)あなたは、自動的に 変数にレコードをアンパックするRecordWildCards拡張子を使用することができます。 同じタイプの2つのレコードを解凍する必要がある場合は役に立ちませんが、覚えておくと便利です。 オリバー・チャールズには素晴らしいブログ・ポストがあります:(link)

(2)サンプルアプリケーションがレコードを折りたたんでいるようです。 ガブリエルゴンザレスのfoldlパッケージを見てください。ブログの記事もあります:

data Foo = Foo { _a :: Int, _b :: String } 

次のコードは、_aフィールドの最大値との 連結を計算します。ここでは(link)

は次のようにあなたは、レコードとそれを使用する方法の例です。 _b_フィールド。

import qualified Control.Foldl as L 
import Data.Profunctor 

data Foo = Foo { _a :: Int, _b :: String } 
    deriving (Show) 

fold_a :: L.Fold Foo Int 
fold_a = lmap _a (L.Fold max 0 id) 

fold_b :: L.Fold Foo String 
fold_b = lmap _b (L.Fold (++) "" id) 

fold_foos :: L.Fold Foo Foo 
fold_foos = Foo <$> fold_a <*> fold_b 

theFoos = [ Foo 1 "a", Foo 3 "b", Foo 2 "c" ] 

test = L.fold fold_foos theFoos 

は、私たちが折り重なるしたい フィールドを抽出するProfunctor機能lmapの使用に注意してください。したがって、

L.Fold max 0 id 

はint型(または任意のNumインスタンス)のリスト倍以上で、かつ:表現

lmap _a (L.Fold max 0 id) 

が同じ倍であるが、我々は_aを使用Fooレコードのリストの上にIntsを製造するために 。

+1

genericlと比較するために 'foldl'ブランチに[https://github.com/rcampbell/vcard/blob/14fb1c91df249ec9aa21ac5cbfe967d7b7a9757d/VCard.hs#L109-L141]を実装しました。 – rcampbell

+0

これはとても素晴らしいです! – ErikR

4

データ型ジェネリックプログラミング技法を使用して、レコードのすべてのフィールドをある種の「一様」な仕方で変換することができます。

おそらく、レコードのすべてのフィールドに、使用するtypeclassが実装されています(典型的な例はShowです)。あるいは、関数を含む "類似の"形の別のレコードがあり、元のレコードの対応するフィールドに各関数を適用する必要があります。

このような用途では、generics-sopライブラリが適しています。それは、sequenceまたはapのような関数のアナログを提供するが、レコードのすべてのフィールドに対して機能する余分なタイプレベルの機械で、GHCのデフォルトのジェネリックス機能を拡張します。

generics-sopを使用して、少し冗長なバージョンのmerge機能を作成しようとしました。いくつかの予備の輸入:ジェネリック-SOPの機能によって使用可能な形式にバイナリ操作を持ち上げる

{-# language TypeOperators #-} 
{-# language DeriveGeneriC#-} 
{-# language TypeFamilies #-} 
{-# language DataKinds #-} 

import Control.Applicative (liftA2) 
import qualified GHC.Generics as GHC 
import Generics.SOP 

helper機能:オペレータと作品のベクトルをとり

fn_2' :: (a -> a -> a) -> (I -.-> (I -.-> I)) a -- I is simply an Identity functor 
fn_2' = fn_2 . liftA2 

一般的なマージ関数Genericを導出任意の単一のコンストラクタ記録に:

merge :: (Generic a, Code a ~ '[ xs ]) => NP (I -.-> (I -.-> I)) xs -> a -> a -> a 
merge funcs reg1 reg2 = 
    case (from reg1, from reg2) of 
     (SOP (Z np1), SOP (Z np2)) -> 
      let npResult = funcs `hap` np1 `hap` np2 
      in to (SOP (Z npResult)) 

Codeタイプレベルの李を返すタイプのファミリーですデータ型の構造を記述するリスト。外側のリストはコンストラクタ用であり、内側のリストには各コンストラクタのフィールドの型が含まれています。

Code a ~ '[ xs ]の制約の一部には、外側のリストにちょうど1つの要素を持たせることによって、 "データ型には1つのコンストラクタしか持てません"と記載されています。

(SOP (Z _)パターンマッチは、レコードのジェネリック表現からフィールド値の(heterogeneus)ベクトルを抽出します。 SOPは "sum-of-products"の略です。

具体例:

data Person = Person 
    { 
     name :: String 
    , age :: Int 
    } deriving (Show,GHC.Generic) 

instance Generic Person -- this Generic is from generics-sop 

mergePerson :: Person -> Person -> Person 
mergePerson = merge (fn_2' (++) :* fn_2' (+) :* Nil) 

Nil:*コンストラクタはオペレータのベクトル(型がn進生成物から、NP呼ばれる)を構築するために使用されます。ベクトルがレコードのフィールド数と一致しない場合、プログラムはコンパイルされません。 はあなたのレコードの種類は非常に均一であることを考えると、操作のベクトルを作成する別の方法は、各フィールドタイプに補助型クラスのインスタンスを定義し、hcpure機能を使用することである。

class Mergeable a where 
    mergeFunc :: a -> a -> a 

instance Mergeable String where 
    mergeFunc = (++) 

instance Mergeable Int where 
    mergeFunc = (+) 

mergePerson :: Person -> Person -> Person 
mergePerson = merge (hcpure (Proxy :: Proxy Mergeable) (fn_2' mergeFunc)) 

hcliftA2hcpurefn_2およびhapを組み合わせた)関数を使用すると、事をさらに単純化することができます。

+0

私はfoldlと比較するために 'generics'ブランチに[https://github.com/rcampbell/vcard/blob/b377d690854ac088ae8fba078971ac732b08cfb8/VCard.hs#L115-L151]を実装しました。 – rcampbell

+0

@rcampbell私はいくつかを単純化するためにコードを更新しました。 – danidiaz

関連する問題