2016-03-28 9 views
3

Sum of Productsでは、レコード関数をどのように取得しますか?レコードのデータ型を持つ以下の例のコード(ghc 7.10.3):一般的なSOPでレコード関数を取得する

{-# LANGUAGE DeriveGeneriC#-} 
import qualified GHC.Generics as GHC 
import Generics.SOP 
data Rec = Rec { frec :: Int, srec :: Maybe String} 
    deriving (Show, GHC.Generic) 

instance Generic Rec  -- empty 
instance HasDatatypeInfo Rec 

は、私たちはGHCiのプロンプトでDataTypeInfoを見てみましょう:frecsrecはコンストラクタFieldInfoを持っているタイプFieldInfoの両方であることを我々は見

*Main> datatypeInfo (Proxy :: Proxy Rec) 
ADT "Main" "Rec" (Record "Rec" (FieldInfo "frec" :* (FieldInfo "srec" :* Nil)) :* Nil) 

ましたfieldNameを文字列として受け取ります。だから、私は実際の機能を得るための方法が表示されませんfrec :: Rec -> Intsrec :: Rec -> Maybe String。私もshow exampleを見ましたが、レコード機能は使用していません。

レコード機能を取得する方法についての説明をお待ちしています(タイプHList '[(Rec -> Int), (Rec -> Maybe String)]のHList)。私がレイアウトしているuser2407038アプローチを使用して突起のうち、機能を取得する方法について型ノットで縛られています質問

補遺。だから、私はさらに、問題に追加したいとします:どのように我々はRecコンストラクタにSOPアプローチを使用して以下のような機能を構築します - 私たちは、レコードのフィールド名と同様に、ここでの機能の両方を使用します。

[ ("frec" ++) . show . frec, ("srec" ++) . show . srec] 

答えて

5

generics-sopライブラリを商品の合計を扱うための一般的なコンビネータを実装しているので、それらのコンビネータを使用してそのような関数を書くべきです。

generics-sopには、レコードに関する情報と型レベルのコンストラクタの情報がありません.GHCジェネリックRepを掘り下げない限り、関数は部分的になります。

この例では、部分的な関数ルートを使用します。それはそれは、ライブラリに含まれるべきであるように思えるが、それはない

data (:*:) f g x = f x :*: g x deriving (Show, Eq, Ord, Functor) 

(または私はそれを見つけることができません):

まず、あなたはこのデータ型を必要としています。

関数の種類が

recordSelectors :: forall t r . (Code t ~ '[ r ], Generic t, HasDatatypeInfo t) 
       => Proxy t -> Maybe (NP (FieldInfo :*: (->) t) r) 

制約Code t ~ '[ r ]なり単にtの生産表現の和がシングルトンリスト(1つのコンストラクタ)であることを述べています。返品タイプはr(レコードフィールドタイプのリスト)の商品で、xFieldInfo xt -> xがあり、rにあります。

一つの実装では、関数は、与えられたデータ型に決定ここ

case datatypeInfo (Proxy :: Proxy t) of 
    ADT _ _ (Record _ fields :* Nil) -> Just $ 
     hzipWith (\nm (Fn prj) -> nm :*: (unI . prj . K . (\(Z x) -> x) . unSOP . from)) 
       fields 
       projections 
    _ -> Nothing 

レコードが本当にある、とそうでないNothingを返します。レコードの場合は、レコードフィールドをzipに、ライブラリの場合はprojectionsとします。これは任意の汎用製品の投影を定義します。残されているのは、それぞれの投影でfrom関数を構成して、「実際の」投影を得ることです。残りの部分(FnunSOPなど)は単なるIDです。


レコード投影関数、関数名を必要とするので、これはさらに簡単です。そして今、関数は部分的ではありません - いずれかのコンストラクタ型は "記録予測"を持っています。

recordSelectors' :: forall t r . (Code t ~ '[ r ], Generic t) 
       => Proxy t -> NP ((->) t) r 
recordSelectors' _ = hmap (\(Fn prj) -> unI . prj . K . (\(Z x) -> x) . unSOP . from) 
          projections 
+0

fieldInfoを無視して投影自体を取得しても、レコード関数を取得するのに十分ではありませんか?あなたが言ったように、ここでの「投影」のタイプは 'NP '[コードレコーディング - > Int、コードレコーディング>多分文字列]'です。また、 'Rep a = SOP I(Code a))'。それを 'to 'とペアにすると、' Rec - > Int'という関数、 'no'が' a'になるはずです。だから私は ':*:'の必要性について混乱している。さらなる洞察をここで感謝します。 – Sal

+0

また、 'ghci'の' projections'の型をどう計算しましたか?そのステップは実験に非常に役立ちます。 – Sal

+0

ああ申し訳ありません - あなたは関数*と*の名前が必要だと思った。確かに、 'NP(( - >)t)r'を持つことができます - それは単なる関数です。また、一般的なタイプの理解に基づいて、GHCiのタイプを計算しませんでした。しかし、もちろん、ghciに尋ねることもできます。 ':projections ::(Code Rec〜 '[xs])=> NP(Projection f xs)xs' - 型は' projections'の一般的な型であり、制約は質問で使われたものと同じです。 ':: NP(Projection f '[Int、Maybe String])' [Int、Maybe String]'と言っています。 – user2407038

関連する問題