2016-11-18 1 views
3
제목 확인을 설명합니다,하지만 난 다음 코드에 대해합니까

완전히 확실하지 :계산의 실용적 펑 대 표현하고 무엇을하지

paket.dependencies :

source https://www.nuget.org/api/v2 
nuget fsharpx.extras 
nuget mongodb.driver 

some.fsx :

#r @".\packages\MongoDB.Bson\lib\net45\MongoDB.Bson.dll" 
#r @".\packages\MongoDB.Driver\lib\net45\MongoDB.Driver.dll" 
#r @".\packages\MongoDB.Driver.Core\lib\net45\MongoDB.Driver.Core.dll" 

#r @".\packages\FSharpX.Extras\lib\net45\FSharpX.Extras.dll" 


open MongoDB 
open MongoDB.Driver 
open MongoDB.Bson 
open MongoDB.Bson.Serialization 

open FSharpx.Choice 

let private createClient (connectString:string) = MongoClient(connectString) 
let CreateClient = protect createClient 

let private getDb name (client:IMongoClient) = client.GetDatabase(name) 
let GetDB1 name client = 
    choose { 
     let! c = client 
     return! (protect (getDb name) c) 
    } 

let GetDB2 name (client:Choice<IMongoClient, exn>) = 
    protect (getDb name) 
    <!> client 

"excersise"에 대한 요점은 GetDB1과 비슷하지만 연산자 (applicants?)를 사용하도록 GetDB2를 작성하는 것이었지만 지금은이를 관리하기 위해 머리를 틀 수 없습니다.

위의 코드는 컴파일하지만 GetDB1 및 GetDB2에 대한 서명은 동일하지 않습니다, 그리고 임은 분명 작성하지 뭔가 을하고.

val GetDB1 : 
    name:string -> 
    client:Choice<#MongoDB.Driver.IMongoClient,exn> -> 
     Choice<MongoDB.Driver.IMongoDatabase,exn> 

val GetDB2 : 
    name:string -> 
    client:Choice<MongoDB.Driver.IMongoClient,exn> -> 
     Choice<Choice<MongoDB.Driver.IMongoDatabase,exn>,exn> 

저는 GetDB2에서 여러 가지 버전과 작업을 시도했지만 위와 같은 서명으로 끝을 맺습니다.

내가 처음에 생각해 보았던 일반적인 생각은 작은 기능을 작성해야만 예외 처리 (보호)를 추가 한 다음 이에 따라 "랩핑"및 "언랩"합니다.

당연히 그렇게 생각지도 못할 수도 있습니다.

향후 연구, 코드 예제 또는 다른 용도로 누군가가 여기를 지시 할 수 있습니까? 모든 유형의 모든 의견이 시점 ;-)

FSharpx doc

부록 I 생각

에서 실제로 환영에있는하지만 MongoDB의 종속성없이, 상기와 거의 동일해야합니다 다음과 같습니다. 당신이 map<!> 연산자를 사용했기 때문에

#r @".\packages\FSharpX.Extras\lib\net45\FSharpX.Extras.dll" 

type DataBase = 
    { 
     Name: string 
    } 

type Client = 
    { 
     connectString: string 
    } with member this.GetDatabase name = { 
         Name = name 
        } 

open FSharpx.Choice 
let private createClient (connectString:string) = { 
    connectString= connectString 
} 

let CreateClient = protect createClient 

let private getDb name (client:Client) = client.GetDatabase name 

let GetDB1 name client = 
    choose { 
     let! c = client 
     return! (protect (getDb name) c) 
    } 

let GetDB2 name client = 
    protect (getDb name) 
    <!> client 
+0

아마도 이것을 [MCVE] (http://stackoverflow.com/help/mcve)로 표현할 수 있습니까? 나는이 질문을 연구하기 위해 MongoDB를 만지작 거리고 싶지 않다 ... –

+1

BTW, F #''은 하스켈의'<$> '대신에 사용된다.이 연산자는 F #에서 합법적 인 연산자가 아니다. 이것은'map'의 중위 버전입니다 (하스켈에서'fmap'). –

+0

@ MarkSeemann hehe. 그것은 사실 MCVE입니다. 또는 그것은 다음과 같습니다 : 여기에 몽고와 함께 할 필요가 없습니다 ;-) 위의 실행은 mongodb가 설치되지 않았거나 pakets이 제자리에있는 경우 전혀 동작하지 않습니다. 그러나 나는 뼈 MCVE에 더 많은 것을하려고 노력할 것이다. ... –

답변

5

현재 유형의 합성을 얻고있다. 즉,는 다음과 같이 정의된다 :

let map f = function 
    | Choice1Of2 value = Choice1Of2 (f value) 
    | Choice2Of2 fail = Choice2Of2 fail 

이것은 즉 기능은 fchoice 형 내부지도 로서 사용 ('T -> 'U) -> Choice<'T,'Failure> -> Choice<'U,'Failure> 서명을 갖는다. 예 :

map (sprintf "%d") 

은 유형이 Choice<int, 'Failure> -> Choice<string, 'Failure>입니다. 이는 Choice 유형을 사용하지 않는 함수를 적용하는 데 유용합니다. 가능한 한 오류 지점이 있으며, 이는 map 호출 전에 발생했습니다.

그러나 다음 함수는 Choice 유형을 생성하지만 Choice 유형은 아닙니다. 즉, 오류가 전파되기를 원한다는 의미입니다. 값에 오류가 있으면 선택하십시오. 값이 괜찮지 만 함수에 오류가 있으면이를 사용하십시오. 모든 것이 성공하면 그것을 사용하십시오. 이렇게하려면 두 가지 오류 유형이 동일해야하며, 두 가지 오류 유형은 (exn)입니다.서명 ('T -> Choice<'U,'Failure>) -> Choice<'T,'Failure> -> Choice<'U,'Failure>으로

let bind f = function 
    | Choice1Of2 value = f value 
    | Choice2Of2 fail = Choice2Of2 fail 

:

이 이렇게 정의 bind 동작을 설명한다.

map과 매우 비슷하지만, 후자의 경우 결과가 Choice1Of2이됩니다. 매핑 된 기능은 항상 성공적입니다. FSharpX에서

, 당신은 |> -like 연산자 >>=, 또는 <| -like 연산자 <<=에 의해 bind에 액세스 할 수 있습니다.

마지막으로 protect은 던져진 예외를 Choice2Of2 exn으로 가져 오는 멋진 방법입니다. 전달 된 함수의 형식이 'T -> 'U 인 점에서 map과 유사하지만 함수가 예외를 throw 할 수 있으며 전달 된 형식이 이 아니고이 아닌 Choice 인 경우에 비슷합니다. protect는 다음과 같이 정의된다 :

let protect f x = 
    try 
     Choice1Of2 (f x) 
    with 
     exn -> Choice2Of2 exn 

그래서 그 서명은 ('T -> 'U) -> 'T -> Choice<'U, exn>입니다.

모든 것이 구현되는 방법에 대한 자세한 내용은 the source of this computation expression을 참조하십시오.


이 모든 것을 종합하면, 왜 당신의 예가 잘못되었는지 알 수 있습니다.

  • getDb name 함수 Client -> DataBase
  • protect (getDb name) 것은 Client -> Choice<DataBase, exn>
  • map (protect (getDb name)) 때문에 map 작품 Choice 내부 따라서 함수 Choice<Client, exn> -> Choice<Choice<DataBase, exn>, 'Failure>하는 기능을한다. 당신이 map을 원하는 'T -> 'U 당신의 매핑 기능은 서명이있는 경우

당신이 원하는,하지만, 일반적으로

let GetDB name client = 
    bind (protect (getDb name)) client 

또는 운영자의 형태로

,

let GetDB name client = client >>= protect (getDb name) 

입니다. 'T -> Choice<'U, 'Failure> 인 경우 bind이 필요합니다.

+0

그 마지막 문장 .... ;-) 고마워! –