What is F#

What is F#

F#는 간결하고 견고하며 성능 좋은 코드를 쓰기 위한 범용 프로그래밍 언어예요. 코드를 깔끔하고 스스로 설명이 되는 형태로 작성하게 해 주면서, 그 과정에서 속도나 호환성을 희생하지 않아요. 실제로 F#은 오픈소스이고 크로스 플랫폼이며, 다른 언어들과도 잘 어울려서 섞어 쓰기 좋은 언어죠.

출처: What is F# - .NET | Microsoft Learn (원문: 영어)

본문

F#이 추구하는 방향을 한마디로 정리하면, 당신이 프로그래밍의 세부 사항이 아니라 풀고 싶은 문제 그 자체에 집중할 수 있게 해 주는 거예요. 코드가 스스로 설명이 되도록 쓰다 보면, 읽는 사람은 무슨 일이 일어나는지 쉽게 따라올 수 있죠.

아래 코드를 함께 볼게요. 이름 목록을 정의하고, 각 이름에 인사말을 만들어 출력하는 흐름이에요.

open System // Gets access to functionality in System namespace.
// Defines a list of names
let names = [ "Peter"; "Julia"; "Xi" ]
// Defines a function that takes a name and produces a greeting.
let getGreeting name = $"Hello, {name}"
// Prints a greeting for each name!
names
|> List.map getGreeting
|> List.iter (fun greeting -> printfn $"{greeting}! Enjoy your F#")

이렇게 F#은 많은 기능을 품고 있는데, 특히 이런 것들이 눈에 띄어요.

  • 간결한 문법(Lightweight syntax)
  • 기본적으로 불변(Immutable by default)
  • 타입 추론과 자동 일반화(Type inference and automatic generalization)
  • 일급 함수(First-class functions)
  • 강력한 데이터 타입(Powerful data types)
  • 패턴 매칭(Pattern matching)
  • 비동기 프로그래밍(Async programming)

이 모든 기능의 전체 목록은 F# 언어 가이드에 상세히 정리되어 있어요.

풍부한 데이터 타입

F#에는 RecordDiscriminated Union(판별 유니온) 같은 타입이 있어서, 데이터를 표현하는 일이 굉장히 자연스러워져요. 예를 들어 출금 결과를 데이터로 나타내 보죠.

// Group data with Records
type SuccessfulWithdrawal =
{ Amount: decimal
Balance: decimal }
type FailedWithdrawal =
{ Amount: decimal
Balance: decimal
IsOverdraft: bool }
// Use discriminated unions to represent data of 1 or more forms
type WithdrawalResult =
| Success of SuccessfulWithdrawal
| InsufficientFunds of FailedWithdrawal
| CardExpired of System.DateTime
| UndisclosedFailure

F#의 record와 discriminated union은 기본적으로 null이 아니고, 불변이며, 비교가 가능해요. 그래서 안심하고 쓰기 정말 편한 타입이죠.

함수와 패턴 매칭으로 맞추는 정확성

F#에서 함수를 정의하는 건 아주 쉬워요. 여기에 패턴 매칭을 더하면, 컴파일러가 강제하는 정확성을 갖춘 동작을 만들 수 있어요. 아래 예시를 볼게요.

// Returns a WithdrawalResult
let withdrawMoney amount = // Implementation elided
let handleWithdrawal amount =
let w = withdrawMoney amount
// The F# compiler enforces accounting for each case!
match w with
| Success s -> printfn $"Successfully withdrew %f{s.Amount}"
| InsufficientFunds f -> printfn $"Failed: balance is %f{f.Balance}"
| CardExpired d -> printfn $"Failed: card expired on {d}"
| UndisclosedFailure -> printfn "Failed: unknown :("

match 문에서 재미있는 점은, F# 컴파일러가 모든 경우를 다 처리하는지 강제로 검사한다는 거예요. 분기 하나를 빠뜨리면 컴파일이 되지 않아서, 실수를 코드가 돌아가기 전에 잡아내죠.

또한 F#의 함수는 일급(first-class) 이에요. 함수를 다른 함수의 파라미터로 넘기거나, 함수에서 함수를 반환할 수도 있다는 뜻이죠.

객체에 동작을 정의하는 함수

F#은 객체도 완전히 지원해요. 데이터와 기능을 한데 섞어야 할 때 객체가 유용해지는데, F#의 멤버(member)와 함수를 이용해 객체를 다루는 동작을 정의할 수 있죠.

type Set<'T when 'T: comparison>(elements: seq<'T>) =
member s.IsEmpty = // Implementation elided
member s.Contains (value) =// Implementation elided
member s.Add (value) = // Implementation elided
// ...
// Further Implementation elided
// ...
interface IEnumerable<'T>
interface IReadOnlyCollection<'T>
module Set =
let isEmpty (set: Set<'T>) = set.IsEmpty
let contains element (set: Set<'T>) = set.Contains(element)
let add value (set: Set<'T>) = set.Add(value)

사실 F#에서는 객체를 함수가 다루는 하나의 타입처럼 취급해서 쓰는 코드가 자주 나와요. 제네릭 인터페이스, 객체 식(object expressions), 그리고 멤버를 절제 있게 활용하는 방식이 큰 규모의 F# 프로그램에서 흔히 보이는 패턴이에요.

더 알아보기

F#의 더 넓은 기능을 살펴보고 싶다면 F# Tour를 확인해 보세요. 언어의 여러 기능을 한 번에 둘러볼 수 있어요.

이 문서의 원본 소스는 GitHub에 공개되어 있고, 이슈나 풀 리퀘스트를 직접 만들고 검토할 수도 있어요. 자세한 내용은 기여자 가이드를 참고하세요.