2P by GN⁺ 11시간전 | ★ favorite | 댓글 1개
  • Rust 스타일 문법을 사용하면서 Go 런타임 위에서 동작하는 소형 언어로, 두 언어의 장점을 결합한 형태
  • 대수적 데이터 타입, 패턴 매칭, Hindley-Milner 타입 시스템, 기본 불변성 등으로 안전성과 표현력을 강화한 구조
  • Go 패키지 직접 import, 파이프라인 연산자, try 블록, task 기반 동시성을 통해 Go 생태계와의 상호운용성 확보
  • 컴파일 타임 오류 탐지, 명확한 진단 메시지, LSP 지원으로 개발자 경험과 코드 안정성 향상
  • Lisette 코드가 읽기 쉬운 Go 코드로 변환되어, 기존 Go 프로젝트와 자연스럽게 통합 가능한 점이 핵심

Lisette 개요

  • LisetteRust 문법을 기반으로 하며 Go 런타임으로 컴파일되는 소형 언어
  • 대수적 데이터 타입, 패턴 매칭, nil 없음, Hindley-Milner 타입 시스템, 기본 불변성, Go 생태계와의 상호운용성을 특징으로 함
  • cargo install lisette 명령으로 설치 가능하며, Go의 fmt, io, os 등 패키지를 직접 import하여 사용 가능

익숙한 문법

  • Rust와 유사한 문법 구조를 가짐
    • enummatch를 통한 패턴 매칭 지원
    • structimpl 블록으로 메서드 정의 가능
  • 표현식 지향 언어로, if, let, 블록 등이 모두 값을 반환함
  • 체이닝과 람다를 지원해 환경 변수 처리나 문자열 조작을 간결하게 표현 가능
  • 인터페이스와 제네릭을 지원하며, interface 정의와 T: Trait 제약을 통한 제네릭 함수 작성 가능
  • if letlet else 구문으로 Option 타입을 간결하게 처리 가능

안전성

  • Go 런타임에서 발생 가능한 오류를 컴파일 타임에 탐지

    • match 문에서 모든 패턴이 처리되지 않으면 오류 발생
    • nil 사용 불가, Option<T> 로 결측값 표현
    • Result 반환값을 무시하면 경고 발생
    • 비공개 타입을 공개 API에서 노출 시 경고
    • 불변 변수를 가변 인자로 전달 시 오류
    • 구조체 필드 누락 시 컴파일 오류
    • 진단 메시지는 구체적 코드 위치와 수정 제안을 함께 제공
    • LSP(Language Server Protocol) 지원으로 VSCode, Neovim, Zed 등 주요 에디터에서 사용 가능

사용성

  • Go와의 상호운용성을 중점으로 설계
  • 파이프라인 연산자(|>) 로 함수 체이닝을 간결하게 표현
  • try 블록을 통해 오류 전파를 단순화
  • 동시성taskChannel을 이용해 Go의 goroutine과 유사하게 구현
  • 직렬화 속성(attribute) 으로 JSON 필드명, 생략, 문자열 변환, 검증 태그 등을 지정 가능
  • panic 복구를 위한 recover 블록 제공, Result 타입으로 안전한 오류 처리 가능
  • defer 구문을 지원해 리소스 정리나 트랜잭션 롤백 보장

투명한 컴파일 결과

  • Lisette 코드는 명확하고 읽기 쉬운 Go 코드로 변환
    • OptionResult 타입은 각각 lisette.Optionlisette.Result 구조체로 변환
    • match 구문은 Go의 조건문으로 변환되어 각 분기 처리
    • ? 연산자는 내부적으로 Result 검사 코드로 치환
  • 예시로, classify 함수는 Option<int>를 받아 Go의 명시적 조건문으로 변환되며, combine 함수는 Result를 검사하는 Go 코드로 변환됨

추가 정보

  • 공식 저장소: github.com/ivov/lisette
  • MIT 라이선스로 공개되어 있으며, 2026년 기준 Iván Ovejero가 개발
Hacker News 의견들
  • 저자와 대화해 봤고 직접 언어를 써보진 않았지만, Lisette가 흥미롭고 Go보다 명확히 개선된 언어로 보임
    다만 Go의 한계를 완전히 극복하긴 어렵다고 생각함. 예를 들어, Go의 인터페이스 타입에서 오는 typed nil 문제는 Lisette에서 Option으로 처리되지만, 이중 언래핑(Some(Some(h)))이 어색해질 수 있음
    또한 Go의 defer 방식은 여전히 불편하며, RAII처럼 자동 자원 해제가 되지 않음
    TypeScript가 JavaScript를 보완했던 이유는 브라우저에서 실행 가능한 대안이 없었기 때문인데, 이제는 WASM이 있어서 상황이 다름
    그래서 “Rust가 있는데 왜 Go를 Rust처럼 만들까?”라는 의문을 가짐. 다만 Lisette은 그 중간 지점을 노리는 듯함
    결국 Lisette은 기존 Go 코드베이스를 개선하거나 Go 런타임을 계속 쓰고 싶은 사람에게 적합한 언어로 보임
    내가 궁금한 점은 “다음 파일을 Go 대신 Lisette으로 작성하려면 어떻게 시작해야 하는가”라는 빠른 시작 가이드의 부재임
    관련 블로그 글: Go is still not good

    • Go는 GC 기반의 동시성 런타임을 제공한다는 점에서 여전히 독보적임
      복잡한 참조 그래프를 다루는 문제 영역에서는 GC가 필수적이며, Go의 사용자 모드 스택 구조 덕분에 효율적인 메모리 모델을 가짐
    • GC 언어에서는 개발 속도가 훨씬 빠름. Rust와 Python으로 챗봇을 만들어봤는데, Rust 경험이 있음에도 Python이 훨씬 빨랐음
      Go는 이런 빠른 CLI 도구 제작에도 적합함 — 예: wordle-tui
    • Go는 언어로서 이상한 점이 많지만, 컴파일 타깃으로는 훌륭함
      문법이 단순하고 크로스플랫폼 지원, 런타임과 GC 내장, “errors as values” 구조, 그린 스레드, 빠른 AOT 컴파일러 등 장점이 많음
      Go의 defer는 유용하지만 에러 처리와 스코프 규칙이 어색함
    • 블로그에서 언급된 “Go가 NULL을 두 번 실수로 만든 언어”라는 표현이 인상적이었음
      TypeScript도 이 문제를 해결하지 못했고, 오히려 더 나쁨. 그래서 직접 Option 타입 패키지를 만들어 NPM에 올림 → fp-sdk
    • Rust의 async는 GC가 없어서 Go보다 덜 편리함. 이 점만으로도 Go 런타임을 선택할 이유가 됨
  • Go로 컴파일되는 언어는 이미 여러 개 있음 — XGo, Borgo, Soppo

    • Borgo와 Lisette은 (T, error) 반환을 Result 타입으로 단순 치환하지만, 이는 의미적으로 완전히 동일하지 않음
      예를 들어 io.Reader.Read(n!=0, io.EOF)가 정상 종료를 의미하므로, 단순히 에러로 처리하면 잘못된 동작을 초래함
    • 컴파일 에러가 타깃 언어에서 소스 언어로 어떻게 전달되는지 궁금함
  • Lisette의 에러 메시지 품질이 인상적임. “help” 힌트가 실제로 유용하게 느껴짐
    다만 Go로 변환된 코드가 장황해질 수 있어서, 런타임 에러 시 Go 코드에서 디버깅해야 하는 점이 걱정됨
    또한 기존 Go 코드에서 Lisette을 호출하는 방향이 어려워 보임
    Lisette이 실험적인 언어인지, 실제 프로덕션을 목표로 하는지 궁금함

    • 개발자가 직접 답변함: lis run --debug 옵션을 사용하면 Go 코드에 //line source.lis:21:5 주석이 삽입되어 스택 트레이스가 원본 Lisette 코드로 매핑
      LSP는 컴파일 타임 에러를 .lis 파일 기준으로 처리함
      현재는 Go에서 Lisette을 호출하는 기능은 없지만, Lisette에서 Go 패키지를 임포트하는 기능이 우선순위임
      처음엔 실험이었지만, 프로덕션 수준 언어로 발전시키는 것이 목표임
  • Rust와 비슷한 문법을 왜 그대로 가져오지 않았는지 궁금함
    예: import "foo.bar" 대신 use foo::bar, Bar.Baz => 대신 Bar::Baz =>
    Rust를 아는 사람은 헷갈리고, 모르는 사람은 Rust로 지식이 전이되지 않음

    • 이런 문법 차이는 사소하며, 핵심은 Rust의 타입 시스템을 Go에 도입하는 것임
      intfloat64는 Go의 타입 명명 규칙을 따른 것임
    • 여러 언어를 오가다 보면 문법 유사성이 오히려 혼란을 유발함. 예를 들어 PHP에서 + 대신 .을 써야 하는 걸 자주 잊음
    • 나도 처음엔 TypeScript 기반의 Rust 스타일 언어를 만들려다, 결국 Rust 자체가 생각보다 어렵지 않음을 깨달음
    • GC 언어에서 Rust 스타일 메모리 모델을 구현하는 건 매우 비자연적임. 객체마다 독립된 주소 공간을 가지는 셈이라 복잡함
    • Lisette은 Rust에서 영감을 받은 언어이지 Rust가 되려는 게 아님. Go 개발자를 주요 타깃으로 함
  • Go 런타임은 좋지만 언어 자체는 투박하고 개선 의지가 없어 보임
    그래서 트랜스파일러를 쓸 정도라면 정말 Go를 싫어해야 할 듯함

  • Rust나 Rust 계열 언어들이 struct와 method를 분리하는 이유가 궁금함
    왜 struct 안에 메서드를 직접 정의하지 못하는지 의문임

    • Rust에서는 struct 필드가 auto-trait에 영향을 주기 때문에, 필드를 한눈에 보는 게 중요함
      또한 impl 블록은 struct와 다른 제네릭 제약을 가질 수 있어서 여러 개를 정의할 수 있음
      마지막으로 Rust는 데이터의 형태(Shape) 를 중심으로 사고하도록 설계된 언어임
    • 개인적으로는 impl 블록이 Go의 메서드와 비슷하다고 느끼며, 어느 쪽이 더 낫다고 보긴 어려움
  • Python처럼 보이지만 Rust나 Go로 컴파일되는 언어가 있다면 정말 멋질 것 같음

    • Mojo는 Swift 창시자가 만든 Python 문법 기반의 고성능 언어
    • Spy는 C로 컴파일되는 초기 시도이며, Nim도 비슷한 계열의 성숙한 언어임
    • Nim은 Python과 비슷한 문법에 정적 타입 시스템을 갖추고, wasm과 C 등 다양한 타깃으로 컴파일됨
    • Static Python Skill은 Python을 정적으로 컴파일하려는 시도임
    • Grumpy는 Google이 만든 Python→Go 트랜스파일러였지만, 9년째 업데이트 없음 (Python 2.7 대상)
  • Lisette은 Go의 단순함과 Rust의 복잡함 사이의 균형을 잘 잡은 언어로 보임
    컴파일 속도가 Go보다 훨씬 느릴 이유가 있는지, Rust 기능 중 어떤 부분을 의도적으로 제외했는지 궁금함
    예: borrow checking, 데이터 타입, async 등

  • Go는 배우기 쉽지만 기능이 부족한 언어임
    Lisette은 그 빈틈을 메우는 언어로 보여서 흥미로움
    TypeScript가 JavaScript를 확장한 것처럼, Go에 표현력 있는 타입 시스템엄격한 컴파일러를 더하면 훌륭한 백엔드 언어가 될 것임
    개인적인 제안은 TypeScript 프론트엔드와의 타입 공유를 지원하는 것임. 이것이 TypeScript가 백엔드에서도 인기 있는 이유 중 하나임

  • Rust의 안전성과 Go의 단순함 사이에서 고민하는 인프라 자동화 개발자로서, Rust의 사용성을 Go 런타임 위에 얹는 아이디어가 매우 매력적임
    프로젝트 발전을 계속 지켜볼 예정임