고급 F#Interop

17998 단어 csharpfsharpdotnet
이것은 나의 개인 블로그에서 내가 소홀히 한 문장의 각색본이다.그것은 처음에 2019년 F#강림절의 일부분이었다.
너는 F를 좋아한다.나는 F를 좋아한다.나도 C#를 좋아한다. 설령 내가 이 두 생태계를 통제한다고 해도 둘이 우호적으로 지내는 것은 쉬운 일이 아니다.더 심각한 것은, 다음 인원이 작성한 코드를 사용해야 할 때...어머, 세상에, 그들은 어디에서 프로그래밍을 배웠습니까?들어봐, 우리 모두 가 봤어.일부 API는 특히 F에서 사용할 때 매우 나쁘게 느껴진다.
그럼 당신은 어떻게 처리합니까?많은 사람들에게 그들은 F로 자신의 대체어를 썼다.

더 좋은 방법이 있다.그리고 그렇게 많은 일이 필요 없어요.
제가 이 글을 쓴 것은 도서관 작가가 가능한 한 이 정보를 이용하여 더 좋은 F#지원을 제공하고 불가능한 상황에서 지역 사회에 더 좋은 F#지원을 제공하기를 바라는 것입니다.

샘플 라이브러리


이 글을 위해서 나는 모든 F#프로그래머들의 가장 무서운 악몽을 썼다. 내가 만들 수 있는 가장 명령적이고 절차적이며 불순한 라이브러리였다.너는 선택의 여지가 없다. 왜냐하면 너의 사장은 바보 같은 사람이기 때문이다.이 라이브러리는 매우 간단하다. 전문적인 창고 유형Double이다.GitHub에서 확인할 수 있습니다.
일반적으로 사람들은 프로그램 집합CLSCompliant을 표시하면 모든 것이 좋으며 모든 언어가 API를 사용할 수 있다고 생각한다.이것은 사실의 절반이다.모든 것이 API를 소모할 수 있습니다.나는 어떤 것이든 더러운 것을 먹을 수 있다는 것을 확신하지만, 이것은 결코 너와 내가 모두 먹고 싶다는 것을 의미하지는 않는다.나는 예시 라이브러리 CLSCompliant 를 표시했지만, 그것은 좋지 않다.나는 작문 중에 죽었다.
API를 살펴보겠습니다.
void Add();
void Add(out Double result);
void Subtract();
void Subtract(out Double result);
void Multiply();
void Multiply(out Double result);
void Divide();
void Divide(out Double result);
//... As well as everything Stack<Double> would already have
얼마나 엉망인지 스택 API로 산술3 * 5 - 8을 해보자.
let stack = DoubleStack()
stack.Push(3.0)
stack.Push(5.0)
stack.Multiply()
stack.Push(8.0)
stack.Subtract()
그래서 이것은 그다지 좋지 않다.그것은 효과가 있을 뿐만 아니라, 서로 다른 언어에서도 같다.하지만, 점원, 이것은 우리가 F에서 사용하고 싶은 그런 API가 아닙니다.그런데 다른 방법은요, 파라미터가 없는 방법은요?
let stack = DoubleStack()
let mutable result = ref 0.0
stack.Push(3.0)
stack.Push(5.0)
stack.Multiply(result)
내가 왜 죽었다고 했는지 알겠어?물론 여기는 희망이 없습니다.물론 이것은 이미 지나갔다. 그것을 받아들여 그대로 사용하든지, 아니면 F 우호적인 API로 자신의 API를 작성하든지.

본고의 전체 과정에서 저는 이를 매우 기능적인 API로 바꾸는 방법을 보여 드리겠습니다. 이것은 F#본기로 작성한 것이 틀림없습니다.

기능 푸시/팝업/미리보기


하나의 좋은 출발점은 우리 자신의 창고 파이프를 실현함으로써 Push (), Pop (), Peek () 의 기능을 정상적으로 느끼게 하는 것이다.
let ( |=> )(stack:DoubleStack)(value) = stack.Push(value)
let stack = DoubleStack() |=> 5.0
그렇듯이, 우리에게는 작업 파이프가 하나 있다!정당하다아니오. 우리는 확실히 단독적인 조작이 있습니다. 그러나 이것은 우리로 하여금 같은 상황에 직면하게 합니다. 서로 다른 코팅층에 있습니다.파이프를 연결하려고 시도하면 문제가 보일 것이다.우리는 함수 호출에서 창고를 되돌려야 한다.
let ( |=> )(stack:DoubleStack)(value) =
    stack.Push(value)
    stack
let stack = DoubleStack() |=> 5.0 |=> 3.0
stack.Multiply()
Assert.Equal(15.0, stack.Peek())
이것은 보기에 이미 많이 좋아진 것 같다.아직 갈 길이 멀다.우리가 언급한 또 다른 두 가지 방법을 고려해 봅시다.
let inline pop (stack:DoubleStack) = stack.Pop()
let inline peek (stack:DoubleStack) = stack.Peek()
이것은 매우 직접적인 번역이다.아주 간단합니다. 그것들은 내연적입니다.이런 방법은 귀속이 가장 쉬우니, 당신은 이미 익숙해졌을 것입니다.이 모든 것이 결합되어 우리가 지금 남은 것은 매우 실용적으로 보이기 시작했지만, 분명히 아직 없다.
let stack = DoubleStack() |=> 5.0 |=> 3.0
stack.Multiply()
Assert.Equal(15.0, peek stack)

함수 대상 초기값 설정 항목


이것은 비록 큰 문제는 아니지만 해결하기 쉽지 않기 때문에 심층적인 문제이다.그래도 너는 대처할 수 있어.봐라, 성명할 때, 파이프가 시작되는 DoubleStack() 은 좀 짜증난다.세상의 종말은 아니지만, 우리는 더 잘할 수 있다.
type Pipeline =
    static member Pipe(left:DoubleStack, right:float) =
        left.Push(right)
        left
    static member Pipe(left:float, right:float) =
        let result = DoubleStack()
        result.Push(left)
        result.Push(right)
        result

let inline private pipe< ^t, ^a when (^t or ^a) : (static member Pipe : ^a * float -> DoubleStack)> left right =
    ((^t or ^a) : (static member Pipe : ^a * float -> DoubleStack)(left, right))

let inline ( |=> )(left:^a)(right:float) = pipe<Pipeline, ^a> left right
이게 도대체 뭐야?나는 이것이 무슨 화려한 흑마법이 아니라고 보증한다.우리 한 번에 한 가지만 봅시다.
우선 우리가 정의한 Pipeline 유형이다.이것은 사용할 함수나 연산자와 같은 가시성을 가져야 한다.그 중에서 우리는 Pipe()의 중량을 정의했다. 그것은 정적이다.당신은 이곳에서 당신이 원하는 모든 것을 정의할 수 있습니다.이것들은 현재 호출되고 있는 실제 방법이다.첫 번째는 우리가 정의한 작업을 실행합니다. 창고와 부동점을 받아들여서 부동점을 창고로 밀어낸 다음 창고로 돌아갑니다.두 번째는 우리가 원하는 행동을 추가했습니다. 창고를 만들고 왼쪽 값을 위로 밀어낸 다음 오른쪽 값을 위로 밀어낸 다음 창고로 돌아갑니다.
두 번째는 내부 연결과 통용되는 수법이다. 이것은 F#s 유형 시스템에 익숙하지 않은 사람들이 가장 두려워하는 일일 수도 있다.나는 그렇게 나쁘지 않다고 보증한다.범용 부분을 무시하면 pipe라는 함수가 있습니다. 그 중에서 두 개의curried 매개 변수가 있습니다: leftright.괜찮네.범용 부분에서 우리는 두 가지 정적 해석 유형을 고려하고 있다. ^t^a.정적 해상도가 중요하기 때문에 이 함수는 절대로 내연되어야 한다.그러나 그것은 볼 필요가 없기 때문에 나는 줄곧 그것을 사유화했다.범형의 나머지 부분은 ^t 또는 ^a 에 서명Pipe이 있는 정적 구성원^a * float -> DoubleStack이 있음을 나타낸다.우리가 방금 토론한 그 방법들을 봐라.^a 그것들의 첫 번째 매개 변수 중 하나와 일치하기만 하면 우리는 일치하는 방법이 있다.이 함수의 정의 부분에서 중복되는 것처럼 보이는 코드는 단지 모듈 매개 변수 (left, right) 로 그것을 호출하여 해석하는 모든 방법을 말한다.됐어, 그건 괜찮아.나는 설령 내가 여전히 이것이 흑마법이라고 생각한다 하더라도.
세 번째 부분은 원시 창고 파이프 조작부호에 대한 작은 수정일 뿐이다.지금도 내연이 필요하다.내연이라는 두 함수는 매우 중요하다.이와 유사하게, 우리는 lefthand 매개 변수를 ^a 로 바꾸어 정적 해석을 할 수 있다.그리고 우리는 흑마법 함수라고 부른다. 우리가 하고 있는 것이 아니라.여기가 ^tPipeline가 작용하는 곳이다.만약 우리가 사용하는 라이브러리가 제3자 라이브러리라고 가정한다면, 우리는 그 안에 Pipe라는 실례 구성원을 추가할 수 없다.우리는 절대 실례 구성원을 추가할 수 없습니다Double!이 추가 매개 변수는 우리가 정의한 유형에 사용되며, 이 유형도 이러한 방법을 가지고 있을 것이다. 이것이 바로 그것이 범용에서 정적 구성원으로 성명되는 이유이다.지금도 우리 자신의 유형을 살펴야 한다는 것을 안다.
이것은 얼마나 큰 진전을 얻었습니까?
let stack = 5.0 |=> 3.0
stack.Multiply()
Assert.Equal(15.0, peek stack)
비록 큰 변화는 없지만, 그것은 확실히 더욱 깨끗해 보인다.더 긴 파이프를 시도해 보세요. 파이프는 여전히 유효합니다.
이 점을 실현하기 위해 또 하나simpler way가 있다.가능하다면 그것을 사용하지만, 나의 경험에 따르면, 많은 경우에 이것은 불가능하다. 이런 방법은 가능하다.이런 더 간단한 방법은 이런 상황에서 작용하지 않는다.
다시 쓰는 방법을 함수에 비추면 가장 유용하기 때문에curry와 파이프 처리를 할 수 있습니다.그래서 이것은 통상적으로 매개 변수를 재배치하는 것을 의미한다.너는 다른 용도를 찾을 수 있다.나는 단지 네가 이렇게 할 수 있기 때문에 건의하지 않는다.

함수 스택 알고리즘


남은 마지막 일은 싫은 산수 방법이다.물론, 지금까지 우리는 마침내 이 기능이 강한 파이프 환경에 완전히 연결할 수 없는 것들을 만났다.정당하다
사실 이것은 매우 간단하다. 우리는 이미 설치했다
let add (stack:DoubleStack) =
    stack.Add()
    stack
등등그렇습니다.아니오, 정말이에요. 그렇습니다.스택 파이프 연산자의 정확한 기호 (|=>) 때문에 Fira Code 나 관련 글꼴을 사용할 때 파이프 화살표처럼 보일 뿐만 아니라 함수 파이프 연산자와 완전히 같은 우선순위와 관련성을 가지기 때문에 새로운 추가가 없습니다.
우리가 가진 모든 것을 하나로 통합:
let stack = 3.0 |=> 5.0 |> mul |=> 8.0 |> sub
Assert.Equal(7.0, peek stack)
나는 너에게 이것이 가능하다고 말했었다.😉
아직 많지만 본고는 많은 내용을 포함하고 있기 때문에 나는 너무 많은 정보를 제공하고 싶지 않다.그래서 미래에 대한 기대가 더 크다.

좋은 웹페이지 즐겨찾기