요약
Copper는 한 개발자가 C 대신 쓰려고 만든, 단순함을 최우선으로 삼은 언어입니다.
개발자 마르크 케르비케가 만들어 BSD 라이선스로 공개한 프로그래밍 언어입니다. 소개 페이지는 사람이 쉽게 이해할 수 있는 언어를 목표로 삼았다고 설명하며, 미니멀리즘을 가장 중요한 기능으로 꼽았습니다.
왜 중요한가
- C를 대신할 언어를 클래스와 제네릭을 갖추면서도 작게 유지할 수 있는지 보여 주는 사례입니다.
- HN에 이 글을 올린 이용자는 한 사람이 자기 용도로 만들어 온 언어라는 점을 흥미로운 대목으로 꼽았습니다.
- 작성자의 블로그와 변경 기록에는 예외 기능을 넣었다가 철회한 설계 과정도 남아 있어 참고할 만하다고 했습니다.
핵심 내용
- 모든 타입에 타입 상속을 쓸 수 있고, 제네릭과 다중 반환값을 지원합니다.
- HN 댓글이 인용한 문서에 따르면 객체지향 언어는 아니지만, 객체지향 설계를 돕는 기능을 제공합니다.
- 헤더 파일 없이 import로 코드를 가져오고, 모든 이름을 전역 네임스페이스에 몰아넣지 않습니다.
- 지연 실행 구문(나중에 실행할 코드를 미리 적어 두는 문법)과 옵션 타입(값이 없을 수 있음을 타입으로 나타내는 방식)을 갖췄습니다.
- 문법은 Ruby와 비슷하게
class,def,end같은 키워드로 구성됩니다.
HN 반응
- 일부 이용자는 변수 선언과 리터럴의 타입 표기가 어긋난다고 지적했고, 작성자는 문서가 낡았을 수 있다며 반박했습니다.
- 컴파일러를 Copper로 작성한 점을 두고, 부트스트래핑(언어를 처음부터 빌드하는 과정)을 망친다는 비판과 C 백엔드가 있으면 괜찮다는 반론이 맞섰습니다.
건설적인 의견을 내고 싶은데, 전부 일관성이 없고 낯설게만 느껴집니다...
예를 들어 변수 선언은 <콜론> <타입> 패턴을 씁니다.
그런데 리터럴은 정반대 문법을 씁니다.
함수의 반환 타입은 func 정의에서는 :로 시작하는데, import한 func 선언에서는 ->로 시작합니다.
제 습관과 거리가 먼 구문이 또 있습니다.
저한테는 아래 쪽이 훨씬 자연스럽습니다.
인자는 값이 아니라 참조로 넘어갑니다. 재귀는 행운을 빕니다.
제작자입니다. 이 언어에 일관성 없는 부분이 아직 많긴 하지만, 말씀하신 것들은 아닙니다.
함수 import도 같은 방식으로 합니다.
문서가 오래됐을 수도 있습니다.
다중 선언은 대입과 일관되게 만든 겁니다.
아래 문법은
다른 많은 언어와 비교하면 특이하긴 한데, 단순한 문법 하나만 쓰기 때문입니다.
타입을 앞에 붙이는 건 리터럴의 타입을 정하기 위해서일 뿐입니다.
제네릭 함수에서 쓸모가 있을 수 있습니다.
다만 초기화하지 않은 변수를 만들 때는 어쩔 수 없이 이 문법을 남겨 뒀습니다.
병렬 대입은 대체로 루비에서 하는 것과 똑같이 동작해서 저는 이상해 보이지 않습니다(애초에
var가 필요하다는 점만 빼면요).나머지는 동의합니다. 다만 재귀에 무슨 문제가 있다는 건지는 모르겠습니다. 정수 같은 기본 타입도 참조로 넘기는 건가요?
저는 그렇게 이해했는데, 솔직히 제가 문서를 잘못 읽었을 수도 있습니다. 이 대목이 그리 명확하지 않습니다.
그렇다면 정수를 참조 자체에 담아 두는 것 같네요. 루비도 그렇게 합니다. 다만 루비는 무엇이 무엇인지를 런타임에 판단해야 해서 정수마다 비트를 몇 개씩 떼어 쓰죠.
파이썬도 그렇게 해요.
그건 튜플 구조 분해(destructuring) 아닌가요?
동감입니다.. 그리고
!문법은 대체 뭔가요? 문서를 봐도 모든 경우에 용도가 분명하지는 않습니다.https://tibleiz.net/copper/documentation.html#basic-syntax
C++의 &와 반대인 것 같습니다. 즉 T!는 객체이고, T는 객체에 대한 참조입니다.
의외로 제가 제일 거슬리는 사소한 점은 파일 확장자 선택입니다. 당연히 ".co"가 아니라 ".cu"여야 하지 않나요? 제가 (가령) Iron이라는 프로그래밍 언어를 만든다면(가상의 이야기고, 그런 이름의 언어가 이미 있을지도 모르지만) 망설임 없이 ".fe"로 정했을 겁니다.
하긴, 저는 제가 만든 장난감 언어의 파일 확장자를 $'.\U0001F9CC'(즉 $'.\xF0\x9F\xA7\x8C')로 정한 사람이긴 합니다만...
Copper로서는 아쉽게도 .cu는 CUDA와 연결되어 있어서, 그래서 피했을지도 모르겠네요.
포르투갈어로 무슨 뜻인지는 묻지 마시고요.
저도 처음에 그 생각을 했어요.
프로그래밍 언어는 아니지만, 제인 스트리트(Jane Street)에는 Iron이라는 코드 리뷰 도구가 있었습니다(지금도 있나요?). 약어는 "fe"입니다.
https://blog.janestreet.com/code-review-that-isnt-boring/
그런 경우가 또 있어요. mercurial은 hg고, the silver searcher는 ag잖아요.
생각하시는 방식이 마음에 드네요.
좀 더 살을 붙인 예제를 만들어 보려 한 점이 좋습니다. 다형성("제네릭")과 인터페이스("OOP")까지 볼 수 있었다면 더 좋았을 겁니다. 특히 언어가 강제하는 가시성 경계(패키지 같은 것)를 넘나드는 경우가 그렇습니다.
제 경험상 추상 인터페이스에 맞춰 프로그래밍하는 것, 그리고 큰 코드베이스에서는 강제되는 가시성 제어가 "OOP"와 "선생님, 이건 C입니다" 사이에서 모두가 원하는 절충점입니다.
서브타이핑과 제네릭이 들어오면 컨테이너가 공변인지 반공변인지 문제가 생깁니다.
왜 그렇죠?
맞습니다. 인터페이스 기반의 (진정한) 객체 간 위임이 있다면 저는 그걸 제일 먼저 쓸 겁니다.
a more fleshed out exemplar (살을 붙인 예시 모델)
exemplar랑 example은 뭐가 다른가요?
between “OOP” and “sir, this is C” ("OOP"와 "선생님, 이건 C입니다" 사이)
그건 오히려 템플릿, 소유권, 연산자 오버로딩 쪽 얘기죠.
다형성은 요즘 방식의 프로그래밍이 아닙니다. 의존성을 만들고 포인터를 따라가느라 속도가 크게 떨어지는데, 둘 다 좋은 프로그램에는 독이니까요.
example은 어떤 개념을 보여 주는 임의의 사례입니다. 문서 하나에 example을 잔뜩 넣어서 가능한 여러 경우를 보여 줄 수 있죠. exemplar는 개념을 모델링하고 설명하는 데 가장 좋다고 골라 둔 방식입니다.
그래서 새 언어의 프로그래밍 교재라면 문자열이 되는 것들의 example, 선언할 법한 구조체의 example을 쓸 겁니다. 한 문제를 푸는 여러 방법을 example로 볼 수도 있겠죠. 반면 exemplar는 그 언어에서 무언가를 하는 가장 좋은 방법, 하다못해 가장 권장되는 방법 하나를 보여 줍니다.
An exemplar is the chosen best way to model and explain a concept. (exemplar는 개념을 모델링하고 설명하는 데 가장 좋다고 골라 둔 방식이다.)
왜 더 나쁜 예시를 쓰는 거죠? 그리고 지금 보고 있는 예시가 최선이라는 건 어떻게 알죠?
example을 여러 개 넣는다면 이상적이지 않은 것도 고르게 됩니다. 모든 example이 최선일 수는 없으니까요. 일부러 나쁜 example이라고 표시해서 보여 줄 수도 있고요. exemplar라는 단어는 "exemplary"(모범적인)라는 형태로 더 익숙한 영어 화자가 많을 겁니다.
내가 든 example이 exemplar인지 알기는 어려울 수 있습니다. 다만 언어 제작자가 예시를 하나만 내놓았다면 적어도 그게 최선이라고 보는 게 합리적입니다. 하나뿐이라면 적어도 문서 안에서는 최선인 거죠.
그분은 'a more fleshed out exemplar'(살을 더 붙인 exemplar)라고 했으니, 지금 있는 건 exemplar가 아니라는 뜻이죠.
Crystal 팬으로서 문법이 정말 마음에 듭니다. 다만 문서만으로는 궁금한 점이 여러 가지 남습니다.
메모리 모델은 어떻게 되나요? 무엇으로 컴파일되나요? 무엇 때문에 '저수준'이라는 건가요? GC가 있나요? C와 ABI 호환이 되거나 FFI가 있나요?
제가 만든 건 아니지만, 한 사람이 자기가 쓰려고 만든 언어라는 점에서 아주 흥미로웠습니다.
제작자의 블로그에 언어 설계 글이 있는 것도 흥미롭습니다(예를 들어 예외 처리에 대한 생각, 그리고 자기 언어에 예외를 넣었다가 결국 접기로 한 이야기). 언어의 변경 이력에서는 진화 과정도 볼 수 있습니다.
https://tibleiz.net/
https://tibleiz.net/copper/changes.html
먼저 양해를 구해야겠습니다. 저는 foo 이야기를 하는 글에서 다들 bar 이야기로 새는 걸 질색하는데, 그런데도 한마디 하겠습니다.
클래스도 있고 암묵적인
self도 있습니다. 없는 건 아니지만, 그런 점에서 Lua나 JavaScript 같은 범용 언어보다 OOP가 더 낫다고 하기는 어려울 것 같습니다.Pony1 같은 언어가 더 성숙하고 기능도 풍부하고 OOP 쪽 완성도도 훨씬 높습니다.
그건 그렇고, a) 남들이 하면 싫어하는 짓을 제가 한 점은 죄송합니다.
b) 언어를 만드는 건 큰일인데, 해냈다는 게 정말 멋집니다.
c) 이미 Copper로 작성되어 있는 것 같은데, 그것도 아주 멋지네요!
"c)"에 대해 말씀드리면, 아닙니다. 셀프 호스팅은 모든 프로그래밍 언어에 가장 큰 유혹이자 최악의 실수입니다. 부트스트래핑에는 죽음이죠. 그래도 유혹에 넘어가 셀프 호스팅을 한다면, Copper처럼 C 백엔드를 두는 게 아마 가장 좋은 대처법일 겁니다.
저는 셀프 호스팅 반대론이 이해된 적이 없습니다.
다음 조건만 충족된다면 말이죠.
그리고
대체 단점이 뭔가요?