2016-08-17 6 views
4

F#をより良くし、Suave.ioの動作をよりよく理解するために、関数を作成するための再利用可能な関数/演算子を作成しようとしています。私はSuaveが> =>演算子を実際に実装して非同期オプションとして動作することを理解していますが、一般化しようとすると楽しいと思いました。静的に解決された型メンバの制約がシステム型の増補を認識しない

以下のコードは、あまりにも多くの情報源から影響を受けています。自分で定義した型ではうまくいきますが、システム型では機能しません。 NullableとOptionの型補完はうまくコンパイルされますが、bind関数のメンバ制約と一致するとは認識されません。

私がOptionで動作させなかったとき、F#で特別なオプションが原因である可能性があるので、Nullableで試してみましたが、悲しいことに葉巻はありませんでした。

fsiからの関連するエラーと出力は、コメントの下のコードにあります。

ご協力いただければ幸いです。

おかげで、 ジョン

open System 

let inline bind (f : ^f) (v : ^v) = 
    (^v : (static member doBind : ^f * ^v -> ^r)(f, v)) 
    // I'd prefer not having to use a tuple in doBind, but I've 
    // been unable to make multi arg member constraint work 

let inline (>=>) f g = f >> (bind g) 

// Example with Result 
type public Result<'a,'b> = 
    | Success of 'a 
    | Error of 'b 

type public Result<'a,'b> with 
    static member inline public doBind (f, v) = 
     match v with 
     | Success s -> f s 
     | Error e -> Error e 

let rF a = if a > 0 then Success a else Error "less than 0" 
let rG a = if a < 10 then Success a else Error "greater than 9" 

let rFG = rF >=> rG 
// val rFG : (int -> Result<int,string>) 

//> rFG 0;; 
//val it : Result<int,string> = Error "less than 0" 
//> rFG 1;; 
//val it : Result<int,string> = Success 1 
//> rFG 10;; 
//val it : Result<int,string> = Error "greater than 9" 
//> rFG 9;; 
//val it : Result<int,string> = Success 9 

// So it works as expected for Result 

// Example with Nullable 

type Nullable<'T when 'T: (new : unit -> 'T) and 'T: struct and 'T:> ValueType> with 
    static member inline public doBind (f, v: Nullable<'T>) = 
     if v.HasValue then f v.Value else Nullable() 

let nF a = if a > 0 then Nullable a else Nullable() 
let nG a = if a < 10 then Nullable a else Nullable() 
let nFG = nF >=> nG 
// error FS0001: The type 'Nullable<int>' does not support the operator 'doBind' 


type Core.Option<'T> with 
    static member inline doBind (f, v) = 
     match v with 
     | Some s -> f s 
     | None -> None 


let oF a = if a > 0 then Some a else None 
let oG a = if a < 10 then Some a else None 

let oFG = oF >=> oG 
// error FS0001: The type 'int option' does not support the operator 'doBind' 
+4

拡張メンバーは、静的に解決された型制約のために考慮されません。ちょうどF#、期間の特徴ではありません。 [uservoiceに投票してください](https://fslang.uservoice.com/forums/245727-f-language/suggestions/5664242-simulate-higher-kinded-polymorphism)。 –

+1

Fyodorが述べたように、これはサポートされていません。これは実際にF#のすばらしい拡張だと思うが、私はあなたがこれについてどのようなユースケースを思いついたのかなんて不思議だ。私はSuaveがとてもシンプルで、あまり抽象的ではないので、オペレータを一般化するとF#コードが改善されるようには聞こえません。 –

+0

@FyodorSoikinありがとう、それはそれを説明します。どのような迷惑な制限、3票がキャストされました:) – JJJ

答えて

3

拡張メソッドは、静的メンバの制約に考慮されないのはなぜ何度も頼まれており、確実にその機能が実装されるまで、それが求められていきます質問ですF#コンパイラ。

this related questionを参照してください。その他の関連する質問へのリンク、およびこの機能をサポートするためにF#コンパイラで何が行われるべきかに関する詳細な説明へのリンク。

具体的なケースでは、上記の回避策で問題が解決され、既にFsControlに実装されています。ここで

はコードです:

#nowarn "3186" 
#r "FsControl.dll" 

open FsControl.Operators 

// Example with Result 
type public Result<'a,'b> = 
    | Success of 'a 
    | Error of 'b 

type public Result<'a,'b> with 
    static member Return v = Success v 
    static member Bind (v, f) = 
     match v with 
     | Success s -> f s 
     | Error e -> Error e 

let rF a = if a > 0 then Success a else Error "less than 0" 
let rG a = if a < 10 then Success a else Error "greater than 9" 

let rFG = rF >=> rG 
// val rFG : (int -> Result<int,string>) 

rFG 0 
//val it : Result<int,string> = Error "less than 0" 
rFG 1 
//val it : Result<int,string> = Success 1 
rFG 10 
//val it : Result<int,string> = Error "greater than 9" 
rFG 9 
//val it : Result<int,string> = Success 9 

// So it works as expected for Result 


// Example with Option 

let oF a = if a > 0 then Some a else None 
// val oF : a:int -> int option 

let oG a = if a < 10 then Some a else None 
// val oG : a:int -> int option 

let oFG = oF >=> oG 
// val oFG : (int -> int option) 

oFG 0 
// val it : int option = None 

oFG 1 
// val it : int option = Some 1 

とにかく、私は既存のChoiceの代わりに、成功/エラーまたはimplementing Success on top of Choiceあなたのケースで、それはこのようになります使用することをお勧めします:

type Result<'a, 'b> = Choice<'a, 'b> 
let Success x :Result<'a, 'b> = Choice1Of2 x 
let Error x :Result<'a, 'b> = Choice2Of2 x 
let (|Success|Error|) = function Choice1Of2 x -> Success x | Choice2Of2 x -> Error x 

そして、あなたが実行することができますがバインドやリターンを書くことなく、あなたの例。

Nullableには例がないなぜあなたは不思議に思うかもしれませんNullableがモナドではないので、よくそれはそれだけで値型上で動作し、機能はとても良く、同じ機能のためにOptionに固執値型ではない、単にです。

+0

詳細な回答ありがとうございます。私は特にリンクとFsControlをチェックアウトします。 – JJJ

+0

@JJJ実際には、選択肢の上に成功を実装する必要はありません(更新された回答を参照)。 これは確かに良いアイデアです。既存の機能をすべて再利用し、他のライブラリとの互換性を高めるからです。 – Gustavo

関連する問題