계산 식(F#)

업데이트: 2010년 12월

F#의 계산 식에서 제공하는 구문을 사용하면 제어 흐름 구문과 바인딩을 통해 순서 지정과 결합이 가능한 계산을 쉽게 작성할 수 있습니다. 함수형 프로그램의 데이터, 컨트롤 및 파생 작업을 관리하는 데 사용할 수 있는 함수형 프로그래밍 기능인 모나드에 대한 구문에도 이러한 계산 식을 편리하게 사용할 수 있습니다.

기본 제공 워크플로

시퀀스 식은 계산 식의 한 가지 예입니다. 또 다른 예로는 비동기 워크플로가 있습니다. 시퀀스 식에 대한 자세한 내용은 시퀀스를 참조하십시오. 비동기 워크플로에 대한 자세한 내용은 비동기 워크플로를 참조하십시오.

몇몇 기능은 시퀀스 식과 비동기 워크플로에 모두 공통으로 사용되며 여기에는 계산 식의 기본 구문이 적용됩니다.

builder-name { expression }

위 구문에 주어진 식은 builder-name으로 지정된 형식의 계산 식입니다. 계산 식은 seq 또는 async 같은 기본 제공 워크플로가 될 수도 있고 사용자가 정의한 워크플로가 될 수도 있습니다. builder-name은 작성기 형식이라고 하는 특수한 형식의 인스턴스에 대한 식별자입니다. 작성기 형식은 계산 식의 각 부분이 결합되는 방식을 관장하는 특수 메서드를 정의하는 클래스 형식, 즉 식의 실행 방식을 제어하는 코드입니다. 또는 루프나 바인딩 등과 같은 여러 가지 F# 구문의 연산을 사용자 지정하는 데 사용할 수 있는 것으로 작성기 형식을 이해할 수도 있습니다.

계산 식에서 몇몇 공용 언어 구문에 대해 사용할 수 있는 형식에는 두 가지가 있습니다. 특정 키워드에 !(느낌표) 접미사를 사용하여 variant 구문을 호출할 수 있습니다. 예를 들면 let!, do! 등과 같은 형식입니다. 이와 같은 특수 형식을 사용하면 작성기 클래스에 정의되어 있는 특정 함수로 해당 연산의 일반적인 기본 동작을 바꿀 수 있습니다. 이러한 형식은 시퀀스 식에 사용되는 yield 키워드의 yield! 형식과 비슷합니다. 자세한 내용은 시퀀스를 참조하십시오.

계산 식의 새 형식 만들기

작성기 클래스를 만들고 클래스에 대한 특수 메서드를 정의하여 고유한 계산 식의 특성을 정의할 수 있습니다. 필요한 경우 작성기 클래스에서 다음 표에 나와 있는 것과 같은 메서드를 정의할 수 있습니다.

다음 표에서는 워크플로 작성기 클래스에 사용할 수 있는 메서드를 설명합니다.

메서드

형식 시그니처

설명

Bind

M<'T> * ('T -> M<'U>) -> M<'U>

계산 식의 let! 및 do!에 대해 호출됩니다.

Delay

(unit -> M<'T>) -> M<'T>

계산 식을 함수로 래핑합니다.

Return

'T -> M<'T>

계산 식의 return에 대해 호출됩니다.

ReturnFrom

M<'T> -> M<'T>

계산 식의 return!에 대해 호출됩니다.

Run

M<'T> -> M<'T> 또는

M<'T> -> 'T

계산 식을 실행합니다.

Combine

M<'T> * M<'T> -> M<'T> 또는

M<unit> * M<'T> -> M<'T>

계산 식의 시퀀스에 대해 호출됩니다.

For

seq<'T> * ('T -> M<'U>) -> M<'U>

seq<'T> * ('T -> M<'U>) -> seq<M<'U>>

계산 식의 for...do 식에 대해 호출됩니다.

TryFinally

M<'T> * (unit -> unit) -> M<'T>

계산 식의 try...finally 식에 대해 호출됩니다.

TryWith

M<'T> * (exn -> M<'T>) -> M<'T>

계산 식의 try...with 식에 대해 호출됩니다.

Using

'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable

계산 식의 use 바인딩에 대해 호출됩니다.

While

(unit -> bool) * M<'T> -> M<'T>

계산 식의 while...do 식에 대해 호출됩니다.

Yield

M<'T> -> M<'T>

계산 식의 yield 식에 대해 호출됩니다.

YieldFrom

M<'T> -> M<'T>

계산 식의 yield! 식에 대해 호출됩니다.

Zero

unit -> M<'T>

계산 식에서 if...then 식의 빈 else 분기에 대해 호출됩니다.

워크플로 작성기 클래스의 많은 메서드는 M<'T> 구문을 사용하고 이를 반환합니다. 이 구문은 일반적으로 비동기 워크플로의 Async<'T> 또는 시퀀스 워크플로의 Seq<'T> 등과 같이 결합 대상인 계산의 종류에 대한 특징을 나타내기 위해 별도로 정의되는 형식입니다. 이러한 메서드의 시그니처를 사용하면 메서드를 서로 결합하고 중첩할 수 있으므로 한 구문으로부터 반환된 워크플로 개체를 다음 구문에 전달할 수 있습니다. 컴파일러는 계산 식을 구문 분석할 때 위 표에 나와 있는 메서드와 계산 식의 코드를 사용하여 해당 식을 일련의 중첩된 함수 호출로 변환합니다.

중첩된 식은 다음과 같은 형식입니다.

builder.Run(builder.Delay(fun () -> {| cexpr |}))

위 코드에서 Run 및 Delay에 대한 호출은 계산 식 작성기 클래스에 정의되어 있지 않은 경우 생략됩니다. 계산 식의 본문(여기에서는 {| cexpr |}로 표시됨)은 다음 표에 설명된 변환에 의해 작성기 클래스의 메서드가 포함된 호출로 변환됩니다. 계산 식 {| cexpr |}은 expr이 F# 식이고 cexpr이 계산 식인 이러한 변환에 따라 재귀적으로 정의됩니다.

변환

{| let binding in cexpr |}

let binding in {| cexpr |}

{| let! pattern = expr in cexpr |}

builder.Bind(expr, (fun pattern -> {| cexpr |}))

{| do! expr in cexpr |}

builder.Bind(expr1, (fun () -> {| cexpr |}))

{| yield expr |}

builder.Yield(expr)

{| yield! expr |}

builder.YieldFrom(expr)

{| return expr |}

builder.Return(expr)

{| return! expr |}

builder.ReturnFrom(expr)

{| use pattern = expr in cexpr |}

builder.Using(expr, (fun pattern -> {| cexpr |}))

{| use! value = expr in cexpr |}

builder.Bind(expr, (fun value -> builder.Using(value, (fun value -> {| cexpr |}))))

{| if expr then cexpr0 |}

if expr then {| cexpr0 |} else binder.Zero()

{| if expr then cexpr0 else cexpr1 |}

if expr then {| cexpr0 |} else {| cexpr1 |}

{| match expr with | pattern_i -> cexpr_i |}

match expr with | pattern_i -> {| cexpr_i |}

{| for pattern in expr do cexpr |}

builder.For(enumeration, (fun pattern -> {| cexpr }|))

{| for identifier = expr1 to expr2 do cexpr |}

builder.For(enumeration, (fun identifier -> {| cexpr }|))

{| while expr do cexpr |}

builder.While(fun () -> expr), builder.Delay({|cexpr |})

{| try cexpr with | pattern_i -> expr_i |}

builder.TryWith(builder.Delay({| cexpr |}), (fun value -> match value with | pattern_i -> expr_i | exn -> reraise exn)))

{| try cexpr finally expr |}

builder.TryFinally(builder.Delay( {| cexpr |}), (fun () -> expr))

{| cexpr1; cexpr2 |}

builder.Combine({|cexpr1 |}, {| cexpr2 |})

{| other-expr; cexpr |}

expr; {| cexpr |}

{| other-expr |}

expr; builder.Zero()

위 표에서 other-expr은 그 외의 경우에 표에 나열되지 않는 식에 대해 설명합니다. 작성기 클래스는 모든 메서드를 구현하고 위 표에 나열된 모든 변환을 지원할 필요가 없습니다. 구현되지 않는 구문은 해당 형식의 계산 식에서 사용할 수 없습니다. 예를 들어 계산 식에서 use 키워드를 지원하지 않으려는 경우 작성기 클래스에서 Use에 대한 정의를 생략할 수 있습니다.

다음 코드 예제에서는 코드의 실행 진행률을 추적하기 위한 콘솔 출력을 생성하는 간단한 계산 식 형식을 만들고 사용하는 방법을 보여 줍니다.

// Program.fs
open Module1

// Create the computation expression object.
let trace1 = trace {
   // A normal let expression (does not call Bind).
   let x = 1
   // A let expression that uses the Bind method.
   let! y = 2
   let sum = x + y
   // return executes the Return method.
   return sum  
   }

// Execute the code. Start with the Delay method.
let result = trace1()

다음 코드에서는 추적 계산 식의 작성기 클래스를 구현합니다.

// Module1.fs
module Module1 =

 // Functions that implement the builder methods.
 let bind value1 function1 = 
     printfn "Binding %A." value1 
     function1 value1

 let result value1 =
     printfn "Returning result: %A" value1
     fun () -> value1

 let delay function1 =
     fun () -> function1()

 // The builder class for the "trace" workflow.
 type TraceBuilder() =
     member x.Bind(value1, function1) = 
         bind value1 function1
     member x.Return(value1)  = result value1
     member x.Delay(function1)   = 
         printfn "Starting traced execution."
         delay function1

 let trace = new TraceBuilder()

이 예제는 다음과 같이 출력됩니다.

Starting traced execution.
Binding 2.
Returning result: 3

참고 항목

기타 리소스

F# 언어 참조

변경 기록

날짜

변경 내용

이유

2010년 12월

Run 메서드 및 변환 표가 추가되었습니다.

고객 의견

2011년 4월

작성기 메서드 표에서 For에 대한 시그니처가 업데이트되었고 Yield가 추가되었습니다.

콘텐츠 버그 수정