TL; DR:異種レコードのさまざまなフィールドから少数のデータ型(おそらくDoubleとBoolのみ)の1つを返すコードを生成する方法を理解するのに助けが必要です。
長い形式:次のデータ型を想定
data Circle = Circle { radius :: Integer, origin :: Point }
data Square = Square { side :: Integer }
といくつかの定型コード
circle = Circle 3 (Point 0 0)
square = Square 5
私は小さなDSLを構築していて、ユーザーに次のようなものを書いてもらいたい
circle.origin
square.side
そしてそれは同様のコードを生成します
origin . circle
side . square
これを解析する場合、たとえば「circle」と「origin」という文字列があります。これらを関数呼び出しに変換する必要があります。私は明らかにこのようなものを持つことができます:
data Expr a = IntegerE (a -> Integer)
| PointE (a -> Point)
lookupF2I "side" = Just $ IntegerE side
lookupF2I "radius" = Just $ IntegerE radius
lookupF2I _ = Nothing
lookupF2P "origin" = Just $ PointE origin
lookupF2P _ = Nothing
返されたデータ型ごとに1つのルックアップ関数があります。データ型ごとに1つの関数を持つことは、実際には2つまたは3つのデータ型しか処理しないという点で、DSLの観点からは実用的です。しかし、これは特に効果的な方法とは思えません。これを(確かに)行うためのより良い方法はありますか?そうでない場合は、フィールドをルックアップできるようにしたいさまざまなレコードから、さまざまなルックアップ関数のコードを生成する方法はありますか?
"circle"
第二に、解析された、または"square"
適切な関数を呼び出す必要がcircle
あるという問題がまだありsquare
ます。型クラスを使用してこれを実装する場合、次のようなことができます。
instance Lookup Circle where
lookupF2I "radius" = Just $ IntegerE radius
lookupF2I _ = Nothing
lookupF2P "origin" = Just $ PointE origin
lookupF2P _ = Nothing
しかし、その場合、ルックアップ関数に適用するタイプを把握する必要があり、さらに悪いことに、これを使用する(多くの)レコードごとにインスタンスを手書きする必要があります。
注:単一のADTを使用して表現できるという事実はCircle
、これが不自然な例であるという点で私の質問に付随しています。Square
実際のコードには、さまざまな非常に異なるレコードが含まれますが、それらに共通するのは、同じタイプのフィールドを持つことだけです。