2014-09-03 3 views
1

좋아, 기본적으로 바인드 연산자를 옵션 유형에 추가하려고하고 있는데, 시도한 모든 항목에 내가 수행하지 못하게하는 몇 가지 확실하지 않은 경고가있는 것으로 보입니다. .NET 유형 시스템의 한계와 관련이 있으며 아마도 사용자 코드에 typeclasses를 구현할 수없는 것과 같은 이유 일 것입니다.유형 확장자에서 연산자 오버로딩

어쨌든, 나는 두 가지 시도를했습니다.

첫째, 난 그냥 다음

let (>>=) m f = ??? 

내가 m의 유형에 따라 다른 일을 할 것인지 실현했습니다. F #은 함수에서 오버로드를 허용하지 않지만 .NET은 메서드에서이를 허용하므로 번호 2를 시도하십시오.

type Mon<'a> = 
    static member Bind(m : Option<'a>, f : ('a -> Option<'b>)) = 
     match m with 
     | None -> None 
     | Some x -> f x 
    static member Bind(m : List<'a>, f : ('a -> List<'b>)) = 
     List.map f m |> List.concat 

let (>>=) m f = Mon.Bind(m, f) 

주사위 없음. 이전에 제공된 유형 정보를 기반으로 고유 한 오버로드를 선택할 수 없습니다. 유형 주석을 추가하십시오.

나는 연산자를 인라인으로 만들려고했으나 여전히 같은 오류가 발생합니다.

그런 다음 >>= 연산자를 유형의 멤버로 만들 수 있다고 생각했습니다. 이 방법이 효과가있을 것이라고 확신하지만 기존 유형을 해킹 할 수 있다고 생각하지 않습니다. type Option<'a> with으로 기존 유형을 확장 할 수 있지만 연산자를 확장자로 사용할 수 없습니다. 이 코드 내 마지막 시도였다

:

type Option<'a> with 
    static member (>>=) (m : Option<'a>, f : ('a -> Option<'b>)) = 
     match m with 
     | None -> None 
     | Some x -> f x 

. "확장 회원 연산자 오버로드를 제공 할 수없는 대신 유형 정의의 일부로 연산자를 정의하는 것이 좋습니다." 굉장해.

다른 옵션이 있습니까? 나는 별도의 모듈에서 다른 모나드를위한 별도의 함수를 정의 할 수 있지만 같은 파일에서 둘 이상의 버전을 사용하려는 경우 지옥처럼 들린다.

+0

@ 존 팔머 오, 죄송합니다. 요점은 Bind 메서드에 대해 두 개 이상의 오버로드가있는 것이 었습니다. 그런 다음 오류가 발생합니다. 나는 그 문제를 해결할 것이다. –

+0

당신의 문제는 타입 생성자가 일반적인 (>> =) 합리적인 타입을 제공한다는 것입니다 (모나드가 친절합니다 * -> *) - 인라인 함수의 정적 제약은 이것 때문에 도움이되지 않습니다 너무 - 아니, 너는 운이 없어. – Carsten

+0

때로는 연산자를 내 모듈 (종종 그냥 연산자라고도 함) 안에 랩핑합니다. 그래서 원하는 경우 MyMonad.Operators를 열거 나 MyMonad.Operators. >> =':(( – Carsten

답변

8

.NET 과부하 해결을 인라인/정적 제약 조건과 결합하여 원하는 동작을 얻을 수 있습니다.

은 다음 단계 explanation에 의해 단계이고 여기에 특정 시나리오에 대한 작은 작업 예제 :

type MonadBind = MonadBind with 
    static member (?<-) (MonadBind, m:Option<'a>, _:Option<'b>) = 
     fun (f:_->Option<'b>) -> 
      match m with 
      | None -> None 
      | Some x -> f x 
    static member (?<-) (MonadBind, m:List<'a>, _:List<'b>) = 
     fun (f:_->List<'b>) -> 
      List.map f m |> List.concat 

let inline (>>=) m f : 'R = ((?<-) MonadBind m Unchecked.defaultof<'R>) f 

[2; 1] >>= (fun x -> [string x; string (x+2)]) // List<string> = ["2"; "4"; "1"; "3"] 
Some 2 >>= (fun x -> Some (string x))   // Option<string> = Some "2" 

또한 '손으로'제약 조건을 지정할 수 있지만 연산자를 사용하여 때 자동으로 추론입니다.

이 기술을 (연산자없이) 구체화하면 FsControl에 Monad, Functor, Arrow 및 기타 추상화를 정의하는 데 사용됩니다.

두 바인딩 정의에 모두 Option.bindList.collect을 직접 사용할 수 있습니다.

+0

좋은 점, FsControl 또는 fsharp-typeclasses를 사용해야합니까? –

+0

@LukaHorvat fsharptypeclasses는 초기 실험/데모 프로젝트입니다. 대신 FsControl을 사용하여 프로덕션 F # 프로젝트 용 코어 lib. – Gustavo

2

왜 "바인딩"(재 정의)해야합니까? 우선 Option.bind가 이미 정의되어 있습니다.

"계산식 작성기"(모나드 "do"구문 sugar에 대한 F # 이름)를 정의하는 데 사용할 수 있습니다. previous answer.