트랜스파일러 시장에서 왜 C#은 실패하고 F#은 성공했을까?

프로젝트에 추가 도입할 생각으로 .NET 계열 언어의 트랜스파일러 프로젝트를 조사하던 중

복수의 C# 트랜스파일러 프로젝트들이 (그나마 명성이 있었던 프로젝트도) 공식 웹사이트와 저장소가 증발하는 등 사실상 파산(…)으로 추정되는 좋지 못한 상황에 놓여있는 반면,

F# 트랜스파일러 프로젝트들이 상업적으로도 많이 성공한 것을 발견했습니다.

Fable · JavaScript you can be proud of!

왜 트랜스파일러라는 잣대를 들이대자 C#은 힘을 못쓰고, F#은 오히려 성공했는지 의아했습니다.

정확한 이유는 아니지만 F#이 기존에 트랜스파일러 프로젝트의 성공 사례로 언급되는 다른 프로젝트들과 많이 닮은 문법을 가지고있다는걸 알게되었습니다.

아래 이미지가 F#의 예시이고

트랜스파일러 프로젝트 중 현대화되어 있다고 평가받는 ReScript의 예시입니다.

제가 프로젝트에 트랜스파일러를 내장했던 이유도 이런 문법을 선호하는 산업용 시장의 신호를 잡아냈기 때문이었습니다.

물론 구체적인 시장 현황은 저도 다 조사하지는 못했지만, F# 트랜스파일러가 성공한 것만 봐도 기회가 되면 더 살펴볼만한 주제인 것 같습니다.

8개의 좋아요

타겟 언어들이 FP 스타일이 주류인 언어들이라서 그런 듯 합니다.

C# 도 fp 스타일이 가능은 하지만, 최근의 업데이트로 완성된 것들이라 널리 퍼지지는 못했습니다.

그러한 발전에도 불구하고, 아직 부족한 면이 있기도 합니다.

클래스 멤버인 함수

함수는 클래스의 멤버로 한정되어 있는데, 이는 OOP의 철학이기도 합니다.

// oop 의 잔재
static class MyTools
{
   public static string Romanize(this string text) => // ...
}

이는 전역 함수를 지칭하는 func 같은 키워드를 도입하면 해결이 가능할 것 같기는 합니다.

namespace MyTools;

// 반환 명시
public func string Romanize(string text) => // ...

// 반환 유추
public func Romanize(string text) => // ..., return str;

기본 콜렉션 부재

// 컴파일 에러
// var collection = [1, 2, 3];

예전에 List<T> 로 확정한다는 계획이 있었는데, 흐지부지된 듯하네요.

개인적으로 유추하건데, IEnumerable<T>, Span<T> 같은 것들이 성능 측면에서 더 좋고, fp 스타일에 더 부합하기 때문인 것 같기도 합니다. fp 언어들이 지원하는 기능이라고 다 좋은 것은 아니니까요.

파이프 부재

접근 연산자 . 와 호출 마커(())를 사용해야 해서, 파이프를 지원하는 언어들과는 차이가 있습니다.

Console.WriteLine("가나다".Romanize().ToUpper());

만약, => 를 파이프로 사용할 수 있게 해준다면,

"가나다" => Romanize => ToUpper => Console.WriteLine;  

기존 인프라와 결합해도 가독성이 떨어지지는 않을 것 같습니다.

// Func<string, string>
var convert = str => str.Romanize() => ToUpper;
//            <--      람다     -->  파이프

convert = (string str) => Romanize(str) => ToUpper;
//        <--      무명 메서드       -->  파이프

//Action<string> 
var print = str => convert => Console.WriteLine => Console.WriteLine;
//                                              void 파이프   

이러한 FP 인프라가 강화되면 코드 스타일이 형식적으로 비슷한 언어로 변환하는 작업 자체가 간단해집니다.

일례로, 일전에 TypeScript 컴파일러를 다시 설계할 때, javascript 코드를 거의 1:1 변환이 가능하다는 효율성 이유만로 golang 이 선택되었습니다.

이러한 FP 스타일 인프라들은, 트랜스 파일러 시장을 위해서라기 보다는, C# 사용자를 위해서라도 도입이 필요하기는 합니다.

FP 스타일 코드는 주로 Gen0 힙 할당하는 경우가 많은데, 이 힙은 스택과 같은 방식(스택 범위를 벗어나면 자동으로 clear)으로 관리되기 때문에 GC 성능 병목이 없습니다.

즉,

FP 스타일 강화 => GEN0 할당 코드 빈도 증가 => 성능 향상

이라는 선순환의 고리가 형성된다는 의미가 되는 것이죠.

5개의 좋아요