2016-10-18 23 views
2

私は次のような問題があります。関数の数が少なく、それぞれの関数が少なくとも2つのパラメータ(それぞれの定義された型の1つ)を必要とする2つの型です。
これらのタイプはパラメータ化されており、ジェネリックタイプのパラメータが適合する場合にのみ、それらの関数で使用できます。fのスレッディング・パラメータ#

type Record<'a, 'b> = { first: 'a; second: 'b } 
type Lens<'a, 'b> = Lens of 'a * 'b 

let someFn lens rec : Record<'a, 'b> = .... 
let anotherFn lens rec : Record<'a, 'b> = ... 
let aThirdFn lens rec : Record<'a, 'b> = ... 
//and potentially a dozen functions more that might or might not return a record 

使用シナリオは、今すぐ任意のワークフローのためのレンズ型は同じままが、レコードは何らかの形で処理されている。この

let workflow1() = 
    let intIntLens = Lens (1, 1) 
    let intIntRec = { first = 10; second = 100} 

    intIntRec 
    |> someFn intIntLens 
    |> anotherFn intIntLens 
    |> aThirdFn intIntLens 

let workflow2() = 
    let strIntLens = Lens ("foo", 1) 
    let strIntRec = { first = "bar"; second = 100} 

    strIntRec 
    |> someFn strIntLens 
    |> someFn strIntLens 
    |> aThirdFn strIntLens 
    |> anotherFn strIntLens 
    |> someFn strIntLens 

のようなものです。 しかし私は何とか退屈なこれらの機能のそれぞれにレンズを通す必要があります。 明白なオプションの1つは、変更可能なモジュールレベル変数を持つことです。私は、並行コードにもかかわらず、ずっと長い間働いていないと思います。

ここで、レンズのパラメータを取り除く最良の方法は何ですか?

答えて

2

あなたの関数は常に取り、(彼ら自身がすべて同じ型の意味)と同じ型を返す場合は、あなただけのそれらのリストを作り、順番に適用できます:

let ap lens fns rec = Seq.fold (fun r f -> f lens r) rec fns 

let workflow2() = 
    let strIntLens = Lens ("foo", 1) 
    let strIntRec = { first = "bar"; second = 100} 

    ap strIntLens [someFn; someFn; aThirdFn; anotherFn; someFn] strIntRec 

私はできませんどのように関数がさまざまな型のものであるかを見てみましょう。しかしこれは単なるおもちゃの例に過ぎず、実際の問題は実際には異なる型の関数を扱います。
この場合、少し面白くない解決方法があります。パイプをローカルに定義して、その中のレンズを閉じます。

let workflow2() = 
    let strIntLens = Lens ("foo", 1) 
    let strIntRec = { first = "bar"; second = 100} 
    let (|*>) r f = f strIntLens r 

    strIntLens 
    |*> someFn 
    |*> someFn 
    |*> aThirdFn 
    |*> anotherFn 
    |*> someFn 

(私は標準パイプ名|>を再利用していないことに注意してください。技術的には可能であるが、コードが読みにくくなるだろうという)

最後に、あなたが問題にすべてのアウトを行く、と組み合わせることができ単一のデータ構造内レンズと入力し、関数を適用され、このような構成で動作するカスタム・パイプを作成するが、トンネルレンズ:

type RecAndLens<'a, 'b> = { rec: Record<'a, 'b>; lens: Lens<'a, 'b> } 
let (|*>) rl f = { rec = f rl.lens rl.rec; lens = rl.lens } 

let workflow2() = 
    ...  
    { rec = strIntRec; lens = strIntLens } 
    |*> someFn 
    |*> someFn 
    |*> aThirdFn 
    |*> anotherFn 
    |*> someFn 

この最後のアプローチは、「長期的な」種類であろう財団の非常に再利用可能なライブラリなどです。

技術的には、概念の間に合わせおよび防止のために、あなただけの代わりにRecAndLensレコードのタプル使用することができます

let (|*>) (rec,lens) f = f lens rec, lens 

let workflow2() = 
    ... 
    (strIntRec, strIntLens) 
    |*> someFn 
    |*> someFn 
    |*> aThirdFn 
    |*> anotherFn 
    |*> someFn 

をしかし、これはあまり音になり、より多くのエラーが発生しやすいが(匂い原始的な強迫観念のビット)。