2

特定のタイプのタスクが実行されるように、一般的な方法でデータ コンストラクターを一致させようとしています。

data Task = TaskTypeA Int | TaskTypeB (Float,Float)

genericTasks :: StateLikeMonad s
genericTasks = do
   want (TaskTypeA 5)

   TaskTypeA #> \input -> do 
       want (TaskTypeB (1.2,4.3))
       runTaskTypeA input

   TaskTypeB #> \(x,y) -> runTaskTypeB x y

main = runTask genericTasks

この中で、genericTasks関数は do 命令を通過し、want何らかの状態モナドによって処理されるもののリストと、(#>)関数を介してそれを行う方法のリストを構築します。このrunTask関数は genericTasks を実行し、結果の to-do と how-to-do のリストを使用して計算を行います。

TaskTypeA,Bただし、後で呼び出すことができるように、(#>) から「タイプ」( ) を抽出する方法を理解するのにかなり苦労しています。を実行する:t TaskTypeAと、 が得られますInt -> Task

つまり、どのように書くの(#>)ですか?

また、私がここで考えていることをそのような一般的な方法で行うことが可能であると完全に確信しているわけではありません. Shake参考までに、ライブラリに(#>)似たものを構築しようとしてい(*>)ます。ただし、Shake は への引数として String を使用する(*>)ため、マッチングは完全に String マッチングを使用して行われます。文字列を必要とせずにやりたいです。

4

1 に答える 1

2

あなたの直感は正しい(#>)です。あなたが指定したように書くことはできません。データ コンストラクターがパターンとして機能するのは、データ コンストラクターがパターン位置にあるときのみです。つまり、関数のパラメーターとして表示されます。

f (TaskTypeA z) = ...

caseステートメントの選択肢の 1 つとして

case tt of
    TaskTypeA z -> ...

またはモナドまたはパターンバインディングで

do TaskTypeA z <- Just tt
   return z

値の位置で (たとえば、関数の引数として) 使用すると、パターン化された性質が失われ、通常の関数になります。つまり、残念ながら、パターンをこれほど簡単に抽象化することはできません。

ただし、パターンの単純な形式化があります。

type Pattern d a = d -> Maybe a

それらを作るのはちょっとした作業です。

taskTypeA :: Pattern Task Int
taskTypeA (TaskTypeA z) = Just z
taskTypeA _ = Nothing

コンストラクタ「転送」(つまり ) も使用する必要がある場合はa -> d、2 つをペアにすることができます (さらに、それを操作するための関数もいくつかあります)。

data Constructor d a = Constructor (a -> d) (d -> Maybe a)

apply :: Constructor d a -> a -> d
apply (Constructor f _) = f

match :: Constructor d a -> d -> Maybe a
match (Constructor _ m) = m

taskTypeA :: Constructor Task Int
taskTypeA = Constructor TaskTypeA $ \case TaskTypeA z -> Just z
                                          _ -> Nothing

これは「プリズム」として知られており、(非常に一般的な形で) lensに実装されています。

このような抽象化を使用することには利点があります。つまり、データ型が許可されているよりも多くの構造を持つ可能性のあるプリズムを構築dできます (たとえば、関数型にすることができます)。コンストラクターで動作する関数を記述して、より単純に構成できます。より複雑なものを一般的に作成するもの。

Constructorただし、単純なデータ型を使用している場合、上記で行ったように、コンストラクターごとにオブジェクトを実装する必要があるのは面倒TaskTypeAです。扱うものがたくさんある場合は、Template Haskellを使用してボイラープレートを作成できます。必要な Template Haskell ルーチンはすでにlens に実装されています。そのため、lens ライブラリの使用方法を学ぶ価値があるかもしれません。(ただし、ナビゲートするのは少し難しいかもしれません)

(スタイル ノート:Constructor上記の 2 番目とその 2 つのヘルパー関数は、ちょっとしたトリックを使用して同等に記述できます。

data Constructor d a = Constructor { apply :: a -> d, match :: d -> Maybe a }

)

この抽象化により、 を記述できるようになりまし(#>)た。簡単な例は次のとおりです。

(#>) :: Constructor d a -> (a -> State d ()) -> State d ()
cons #> f = do
    d <- get
    case match cons d of
        Nothing -> return ()
        Just a  -> f a

または、正確に何を望んでいるかに応じて、より洗練されたものになるかもしれません。

于 2013-07-19T20:11:24.930 に答える