3

テキスト ファイルを処理するデータ パイプラインを作成しているとします。次のタイプと機能があります。

data A = A deriving (Show, Typeable)
data B = B deriving (Show, Typeable)
data C = C deriving (Show, Typeable)
data D = D deriving (Show, Typeable)

step1 :: A -> B
step2 :: B -> C
step3 :: C -> D

以下の関数 step{1..3} のそれぞれについて、次のようなことを行って、既存のファイルから新しいファイルを生成できるようにしたいと考えています。

interact (lines . map (show . step . read) . unlines)

次に、それらをグラフに配置して(関数に複数の入力を持たせることができるようにする)、次のことを実現したいと考えています。

  1. データ構造を走査して、どの関数が他のどの関数に入力を提供しているかを判断できます

  2. データ構造はコンパイル時に型チェックを行うため、無効な配置があると型エラーがスローされます。

2 なしで 1 を行う方法 (共通の型クラスを与える) と、1 なしで 2 を行う方法 ((.) を使用するだけ) は知っていますが、両方を同時に行う方法はわかりません。何か案は?ティア。

アップデート

AndrewCの答えはかなり的を射ており、Mapを使用してメタデータ(名前)のグラフを作成し、同時にそれを個別に型チェックできるため、ほとんどの方法でそこに到達できますが、柔軟なポリモーフィズムも必要です。以下の例はこれを解決しますが、2 つの重大な注意事項があります。

  1. ステップ 4 のパターン マッチングを徹底的に行う必要はありません。
  2. 面倒です。これらの「BorC」スタイルのポリモーフィック型は、ポリモーフィズムを有効にする以外には何もせず、あまり安全ではありません (1 を参照)。型クラスのメンバー (Step4Inputと のインスタンスを使用B)でパターン マッチを試みましCたが、うまくいきませんでした ( と言いましたCouldn't match type B with C)。
data BorC = ItsB B | ItsC C

step4 :: BorC -> D
step4 x = case x of { ItsB b -> D; ItsC c -> D }

-- step1    step2
--    \       /
--   ItsB   ItsC
--     \   /
--     step4
4

1 に答える 1

2

私はあなたの問題を誤解しているかもしれませんが、単に関数を名前でラップしてみませんか?
多くのことをいじる必要のない状況への非常に簡単なアプローチのようです。

module NamedFunctions where

import Control.Category        -- so I can generalise (.)
import Prelude hiding ((.),id) -- don't worry, available from Category

関数自体とともに作成した関数名のリストを保存します。

data Fn name a b = Fn {names::[name],apply:: (a -> b)} 

関数を表示するときは、そこに到達するために作成したものを表示するだけです:

instance Show name => Show (Fn name a b) where
  show f = show $ names f

との新しいバージョンを定義(.)($)ます。

infixr 9 ...
(...) :: Fn name b c -> Fn name a b -> Fn name a c
f ... g = Fn (names f ++ names g) (apply f . apply g)

infixr 0 $$$
($$$) :: Fn name a b -> a -> b
f $$$ x = apply f x

(.)名前付き関数で作業するために再利用します

instance Category (Fn name) where
   (.) = (...)
   id = Fn [] id

気にしない関数は名前を付けずに残して、namesリストに貢献しないようにすることができます。

name  n f = Fn [n] f
unnamed f = Fn []  f

好きなデータを使用して各関数に名前を付けることができます。次に例を示します。

timesThree = name "(*3)" (*3)
addFour = name "(+4)" (+4)

step1 :: Fn Int A B 
step1 = name 1 undefined
step2 :: Fn Int B C 
step2 = name 2 undefined
step3 :: Fn Int C D 
step3 = name 3 undefined


*NamedFunctions> timesThree . addFour
["(*3)","(+4)"]
*NamedFunctions> timesThree . addFour $$$ 5
27
*NamedFunctions> step3.step2.step1
[3,2,1]
*NamedFunctions> step3.step2.step1 $$$ A
*** Exception: Prelude.undefined
于 2013-07-20T13:34:41.557 に答える