5

要素の数や内容がわからない単純なタプル(DBからの読み取りなど)があります。例 (String, Int, Int)または(String, Float, String, Int)

あらゆる種類のタプルを受け取り、すべてのデータを文字列「NIL」に置き換えるジェネリック関数を作成したいと思います。文字列「NIL」がすでに存在する場合は、そのままにしておく必要があります。

例に戻ると、次のようになります ("something", 3, 4.788)("something", "NIL", "NIL")

("something else", "Hello", "NIL", (4,6))結果として("something else", "NIL", "NIL", "NIL")

既知のタプルを使用してこれを行うことは問題にならないため、どこから始めればよいかは明らかにわかりません。ここで、Template Haskellなしで希望の結果を得ることができますか?

4

4 に答える 4

8

を使用することは可能GHC.Genericsですが、ここで他の推奨事項よりも推奨することはしませんが、完全を期すためにここに文書化すると思いました。

アイデアは、タプルをパターンマッチできるものに変換することです。典型的な方法 (私がHList使用していると思います) は、n-タプルからネストされたタプルに変換することです: (,,,)-> (,(,(,))).

GHC.Generics:*:タプルを製品コンストラクターのネストされたアプリケーションに変換することで、同様のことを行います。toおよびfromは、値をその一般的な表現との間で変換する関数です。K1タプル フィールドは一般的にnewtypes で表されるため、メタデータ ( ) ノードM1と製品 ( :*:) ノードのツリーを再帰的に下にたどり、リーフ ノード (定数) を見つけてK1その内容を "NIL" 文字列に置き換えます。 .

type 関数は、Rewrite型をどのように変更するかを記述します。Rewrite (K1 i c) = K1 i String各値 (c型パラメーター) をString.

小さなテストアプリが与えられた場合:

y0 :: (String, Int, Double)
y0 = ("something", 3, 4.788)

y1 :: (String, String, String, (Int, Int))
y1 = ("something else", "Hello", "NIL", (4,6))

main :: IO ()
main = do
  print (rewrite_ y0 :: (String, String, String))
  print (rewrite_ y1 :: (String, String, String, String))

一般的なリライターを使用して、以下を生成できます。

*メイン> :メイン
(「何か」、「NIL」、「NIL」)
(「その他」、「NIL」、「NIL」、「NIL」)

組み込みGenerics機能と型クラスを使用して実際の変換を行います。

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

import Data.Typeable
import GHC.Generics

rewrite_
  :: (Generic a, Generic b, Rewriter (Rep a), Rewrite (Rep a) ~ Rep b)
  => a -> b
rewrite_ = to . rewrite False . from

class Rewriter f where
  type Rewrite f :: * -> *
  rewrite :: Bool -> f a -> (Rewrite f) a

instance Rewriter f => Rewriter (M1 i c f) where
  type Rewrite (M1 i c f) = M1 i c (Rewrite f)
  rewrite x = M1 . rewrite x . unM1

instance Typeable c => Rewriter (K1 i c) where
  type Rewrite (K1 i c) = K1 i String
  rewrite False (K1 x) | Just val <- cast x = K1 val
  rewrite _ _ = K1 "NIL"

instance (Rewriter a, Rewriter b) => Rewriter (a :*: b) where
  type Rewrite (a :*: b) = Rewrite a :*: Rewrite b
  rewrite x (a :*: b) = rewrite x a :*: rewrite True b

この例で使用されていないいくつかのインスタンスは、他のデータ型に必要です。

instance Rewriter U1 where
  type Rewrite U1 = U1
  rewrite _ U1 = U1

instance (Rewriter a, Rewriter b) => Rewriter (a :+: b) where
  type Rewrite (a :+: b) = Rewrite a :+: Rewrite b
  rewrite x (L1 a) = L1 (rewrite x a)
  rewrite x (R1 b) = R1 (rewrite x b)

もう少し努力Typeableすれば、インスタンスから制約を取り除くことができますK1。Overlapping/UndecidableInstances のために、より良いかどうかは議論の余地があります。GHC は結果の型を推論することもできませんが、できるはずです。いずれにせよ、結果の型が正しい必要があります。正しくないと、読みにくいエラー メッセージが表示されます。

于 2012-11-18T08:50:20.210 に答える
5

タプルを使用する代わりに、HListまたはVinylパッケージをご覧ください。

VinylにはGHC7.6が必要であり、HListのアップデートが間もなく予定されています(最新のHaskell Community Activities Reportによる)。特にHListは、SQLクエリの結果を表すのに適しているようです。

HListについて:

HListは、拡張可能な多形レコードやバリアントを含む、型指定された異種コレクション用の包括的で汎用的なHaskellライブラリです。HListは、標準のリストライブラリに類似しており、さまざまな構築、ルックアップ、フィルタリング、および反復プリミティブのホストを提供します。通常のリストとは対照的に、異種リストの要素は同じタイプである必要はありません。HListを使用すると、ユーザーは静的にチェック可能な制約を作成できます。たとえば、コレクションの2つの要素が同じタイプを持つことはできません(したがって、要素はタイプによって明確にインデックス付けできます)。

..。

2012年10月バージョンのHListライブラリは、GHC7.4以降で提供されるより洗練されたタイプを利用するための大幅な書き直しを示しています。HListは現在、型レベルのブール値、自然数とリスト、および種類のポリモーフィズムに依存しています。多くの操作が型関数として実装されています。もう1つの注目すべき追加は、異種リストの展開です。現在、多くの操作(射影、分割)が展開の観点から実装されています。このようなリファクタリングにより、実行時のオーバーヘッドなしで、より多くの計算がタイプレベルに移行しました。

于 2012-11-18T07:01:52.657 に答える
4

誰かがコメントでこれについて言及しましたが、おそらくタプルの代わりにリストを使用する必要があります。あなたが使う:

data MyType = S String | D Double

someData :: [MyType]

次に、単純なを使用してリストを変換できますmap

convert :: MyType -> String
convert (S str) = str
convert _       = "NIL"

convertList :: [MyType] -> [String]
convertList = map convert

また、値のソースのタプルサイズが何であるかがわからない理由もわかりません。おそらくそれを明確にする必要があります。

于 2012-11-18T15:16:00.500 に答える
3

Haskell では、すべてのタプルが別の型であるため、TH を使用せずに簡単な方法で関数を作成することはできないと思います。また、GHC は、許可されるタプルの最大サイズに制限を課します。Haskell 標準では、コンパイラは少なくともサイズ 15 までのタプルを許可する必要があるとのみ述べています。

Prelude> let a = (1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0)

<interactive>:2:9:
    A 70-tuple is too large for GHC
      (max size is 62)
      Workaround: use nested tuples or define a data type

したがって、タプルを使用する代わりに、他のデータ型を使用してみるべきだと思います。

于 2012-11-18T02:47:57.540 に答える