Unison 언어의 타입클래스, FFI, 분산 실행, 코드베이스 모델, 빌드/설치, IDE 및 기여 방법 등에 대한 자주 묻는 질문과 답변을 정리한 문서입니다.
아직은 없습니다. 최소한, 아직은요. 경계 다형성(bounded polymorphism)을 표현할 수 있는 방법을 어떻게 제공할지 여러 가지를 검토 중입니다. 초기 관찰 내용은 이 글을 참고하세요.
현재 어느 정도까지 구체화되어 있는 아이디어는 두 가지가 있습니다.
mconcat : [a] ->{Monoid a} a 여기서 Monoid a는 ability입니다.mconcat : Monoid a -> [a] -> a 여기서 Monoid a는 레코드 타입입니다.둘 다 Monoid a 핸들러/값을 자동으로 공급하기 위한 일련의 언어 확장이 필요합니다.
프로그래밍 언어로서 Unison은 Haskell의 후손으로 볼 수 있으며, 다음과 같은 많은 유사점을 갖습니다.
주요 차이점은 다음과 같습니다.
do 키워드는 지연 계산으로서의 코드 블록을 시작하는 것이며, Haskell의 “모나드용 do 표기법”과는 일치하지 않습니다.let 키워드는 코드 블록을 시작하는 역할을 합니다. 변수 정의의 시작이 아닙니다.🌻 두 언어 사이에 유사점이 있긴 하지만, Unison으로 프로그래밍하려면 Haskell을 알고 있어야 한다는 기대는 전혀 없습니다.
현재 작업 중입니다! Unison은 다른 언어로 작성된 코드를 호출하기 위한 FFI(Foreign Function Interface)를 추가하는 중입니다. 아직 초기 단계이므로, 궁금하고 일찍 써 보고 싶다면 Discord의 #toolchain-development 채널을 방문해 보세요.
프로그램은 IO ability를 통해 외부 세계와 상호작용할 수도 있으며, 여기에는 네트워크 소켓을 통한 상호작용도 포함됩니다. 따라서, 예를 들어 웹 서비스와 같이 네트워크 인터페이스를 제공할 수 있는 다른 언어의 코드를 호출할 수 있습니다.
외부 API가 어떻게 동작하게 될지에 대한 대략적인 설계는 다음과 같습니다.
GPU를 통해 노출된다고 합시다.'{GPU} () 타입의 프로그램을 '{IO} ()를 실행하듯이 실행할 수 있게 해 줍니다.아직 더 연구해야 할 부분이 많습니다. 예를 들면 다음과 같습니다.
ucm 링크 종속성 관리ability 메커니즘을 사용하는 점에 주목해 주세요. 이는 테스트 관점에서 매우 훌륭한 스토리를 제공합니다. 순수 Unison으로 GPU ability를 처리하는 테스트 핸들러를 정의하고, 이를 사용해 GPU 코드에 대해 일반 Unison 테스트를 추가할 수 있습니다. 이 테스트들은 적절한 GPU가 설치되었는지와 상관없이 어디에서나 실행될 수 있습니다.
현재로서는, 사용자가 자신의 클러스터에서 분산 Unison 프로그램을 손쉽게 실행하고 관리할 수 있는 방법은 없습니다. 하지만, UCM을 사용해 로컬 컴퓨터에서 일반 Unison 프로그램 또는 단일 머신 Cloud 프로그램을 실행할 수 있고, Docker를 통해서 실행할 수도 있습니다.
Cloud API를 사용해 Unison 프로그램을 자신이 가진 인프라에서 실행하고 싶다면, bring-your-own-Cloud가 곧 Unison에 도입될 예정입니다. 팀과 개인은 온프레미스 환경이나 자신의 클라우드 제공업체 계정에서 Unison Cloud 프로그램을 실행할 수 있게 됩니다.
Unison 팀은 서비스로서의 클라우드 컴퓨팅을 제공하기 위해 Unison Cloud Platform을 개발했습니다. Cloud의 로컬 시뮬레이션은 Cloud 클라이언트 라이브러리를 통해 지원됩니다.
Cloud 사용 신청은 여기에서 할 수 있습니다. Unison Cloud는 저희의 PaaS(Platform as a Service) 제품이며, 그 성공이 Unison 언어와 도구, 생태계 전반의 오픈 소스 개발을 지속 가능하게 뒷받침해 주기를 기대하고 있습니다.
여기에는 사실 여러 가지 질문이 있습니다. 아래에 몇 가지를 나열했습니다. 일부 측면은 더 많은 연구가 필요하지만, 전반적으로 Unison을 사용해 안전한 시스템을 구축하는 것이 가능하다고 믿고 있습니다.
Unison 노드는, 계산을 보내오는 피어가 (a) 자기 자신을 올바르게 식별하는지, (b) 해당 노드의 컴퓨트 자원을 사용할 권한이 있는지, (c) 노드의 데이터에 접근하거나 효과를 수행할 권한이 있는지를 어떻게 보장할 수 있을까요?
원격 노드에서 온 계산은 샌드박스 안에서 실행되며, 그 샌드박스에는 리소스와 capability 권한이 부여됩니다. 원격 노드를 인증하고 특정 용도에 대해 인가하는 시스템이 제공될 것입니다.
Unison 노드는, 피어에서 동기화해 온 코드가 안전하게 실행될 수 있고, 발신자가 인가받은 리소스나 권한만 사용할 것임을 어떻게 알 수 있을까요?
리소스 사용은 샌드박스 메커니즘을 통해 동적으로 제한되며, 코드가 한도를 초과하면 실행이 제한되거나 취소됩니다. 프로그램이 효과와 I/O를 사용할 수 있으려면, 샌드박스가 명시적으로 그 ability를 제공해야 합니다. Unison의 타입 시스템은 프로그램이 부여받은 ability만 사용할 수 있도록 강제합니다.
Unison 코드/데이터의 일부를 특정 노드에 비공개로 유지해서, 다른 노드로 동기화되지 않게 할 수 있을까요?
데이터의 경우, "pinned data" 기능을 추가할 계획이 있습니다. 이와 관련해서는 이슈 #798을 참고하세요.
코드의 경우, 이는 활발히 연구 중인 주제입니다. 이슈 #799을 참고하세요.
Unison이 코드와 데이터를 피어로 전송한다는 점을 감안할 때, 취약점을 막기 위해 Unison에서 특별한 코드 패턴이 필요한가요?
물론 필요합니다. 그리고 분산 실행 API를 사용하면서 이러한 패턴에 대한 이해를 쌓아 나가는 것은 분명 재미있는 일이 될 것입니다!
각 노드에서 Unison 런타임 환경이 실행 중이어야 합니다. 이 환경은 다른 Unison 피어의 네트워크 연결을 받아들이고, 코드 동기화와 분산 평가 프로토콜을 수행합니다.
클라우드 컴퓨팅 리소스와 상호작용하기 위한 API가 궁금하다면 Cloud 클라이언트 라이브러리를 살펴보세요.
이 영역은 Unison에서 많은 고민과 연구가 이루어져 왔고, 현재도 활발히 개발이 진행 중입니다.
unison-nix 리포지토리를 참고하세요.
Nix는 공식적으로 지원되지는 않지만, nix 팬들과 Unison 팬들 사이에 상당한 교집합이 있는 것으로 보입니다. 그러니 자유롭게 시도해 보시고, 막히는 부분이 있으면 Discord에서 질문해 주세요.
ucm이 반응하지 않습니다이는 ucm을 해당 스크래치 파일이 있는 디렉터리가 아닌 다른 디렉터리에서 시작했을 때 발생할 수 있습니다. scratch.u가 있는 디렉터리에서 ucm을 시작했는지 확인하세요.
리눅스의 경우, 시스템이 inotify watches를 소진했을 때 이런 현상이 일어날 수 있습니다. (백업 애플리케이션이 이를 많이 사용하는 경우가 흔합니다.)
이 경우인지 확인하려면, 임의의 파일에 대해 tail -f <file>을 실행하고, 다음과 같은 출력을 확인해 보세요.
tail: inotify cannot be used, reverting to polling: Too many open files
만약 그렇다면, 다음 명령으로 한도를 올릴 수 있습니다.
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
위 명령이 동작하지 않으면 커뮤니티 Discord에서 저희에게 연락 주세요.
정의는 절대 바뀌지 않지만, 우리가 그 정의에 붙이는 이름은 바뀝니다. 예를 들어, 해시가 #1krif5인 term이 있고, 여기에 foo라는 이름을 붙였다고 해 봅시다. 이를 다음과 같이 그릴 수 있습니다.
foo -> #1krif5
그런데 foo의 코드에 버그가 있다는 생각이 들어, 새로운 정의를 Unison에 제시한다고 합시다. 그러면 Unison은 새로운 foo 정의의 해시를 계산할 텐데, 이 해시가 #amrl61이라고 하면, 이름 foo를 이 새 해시를 가리키도록 바꿉니다. 그러면 다음과 같은 상태가 됩니다.
#1krif5
foo -> #amrl61
즉, 어떤 term도 바뀌지 않았고, 새 term이 하나 추가되었으며, 이름 하나가 다른 term을 가리키도록 바뀐 것입니다.
좋습니다. 그런데 foo를 호출하던 함수 bar는 어떻게 될까요?
사실 처음에는 다음과 같은 상태에서 시작했다고 합시다. 하나의 term이 다른 term을 참조(호출)하고 있고, 이 둘은 각각 bar, foo라고 불립니다.
foo -> #1krif5
↑
bar -> #doq7s1
이 상태에서 foo를 수정하면, 잠시 동안 코드베이스는 다음과 같은 상태에 놓이게 됩니다. bar는 아직 예전 버전의 foo를 참조하고 있습니다.
#1krif5
↑
bar -> #doq7s1
foo -> #amrl61
하지만 Unison은 이것이 최종 상태라고 생각하지 않습니다. 실제로는, 오래된 term #1krif5의 타동적 의존자(transitive dependents) 집합에 대해, 갱신된 foo 정의를 전파(propagate) 하길 원한다고 가정합니다.
옛 foo와 새 foo가 동일(또는 호환되는) 타입을 갖는 한, Unison은 이 전파를 자동으로 수행하고, 다음과 같은 상태를 남깁니다.
(unused) #1krif5
bar -> #doq7s1
↓
foo -> #amrl61
이 전파는 재귀적으로 동작하므로, bar의 의존자들, 그리고 그들의 의존자들 등도 함께 처리됩니다.
만약 타입이 호환되지 않는다면, Unison은 변경을 자동으로 전파할 수 없습니다. 예를 들어, 옛 bar 정의의 타입이 Int, 새 정의의 타입이 Nat이고, bar가 foo가 Int이기를 요구한다고 합시다. 그러면 UCM은 스크래치 파일에서 이 불일치를 해결하라고 알려 줍니다.
이 타입 불일치는 bar를 수정(정확히는, 이름 bar를 새 정의를 가리키도록 바꾸는 것)해서, foo가 Nat이더라도 잘 동작하도록 하면 해결할 수 있습니다. 이때 bar의 타입이 그대로 유지된다면, 그 다음부터는 Unison이 자동으로 전파 과정을 이어갈 수 있습니다.
주의할 점은, 이런 편집 과정 중의 과도기 상태에서도 코드베이스는 여전히 유효하고, 모든 테스트는 여전히 통과한다는 것입니다. 단지, 최소한 일부 테스트는 여전히 예전 정의의 foo와 bar를 참조하고 있을 뿐입니다.
더 자세한 내용은 충돌 해결에 관한 문서를 참고하세요.
여러분의 이름이 역사에 남을 것입니다!
Unison은 term과 타입을 해싱하기 위해 512비트 SHA3 다이제스트를 사용합니다. 두 Unison 객체가 같은 다이제스트로 해시될 가능성은 상상할 수 없을 만큼 작습니다. 매초 10억 개의 고유한 Unison term을 쓴다고 해도, 해시 충돌은 대략 100조 년에 한 번 정도 기대할 수 있습니다.
그래도 만약 충돌이 실제로 일어난다면, term을 약간만 바꿔도 다른 해시를 얻을 수 있습니다. 예를 들어, term을 (아무 동작도 하지 않는) identity 함수 호출로 감싼다거나, {{wow this is unlikely!}}와 같은 문서 리터럴을 term에 추가할 수 있습니다.
복사가 많이 일어나지 않나요?
코드베이스는 요소가 결코 바뀌지 않는 불변 자료구조이기 때문에, 함수형 프로그래밍에서 익숙한 구조적 공유(structural sharing)를 그대로 얻을 수 있습니다.
실제로, Unison은 전통적인 언어들보다 디스크 공간을 훨씬 덜 낭비할 수도 있습니다. Unison에는 빌드라는 개념이 없기 때문에, 빌드 아티팩트를 생성하지 않기 때문입니다.
코드를 지울 수 있는 방법이 있나요?
코드베이스는 git과 마찬가지로 완전한 히스토리를 저장합니다. 덕분에 코드베이스를 현재 상태로 이끈 모든 작업의 전체 뷰를 얻을 수 있습니다. 또한, 코드베이스에서 특정 정의를 해시로 참조하면, 그 참조가 결코 미정의 상태가 되지 않는다는 보장도 얻습니다.
앞으로는, 코드베이스 내부에서 들어오는 참조가 전혀 없는 정의를 제거할 수 있게 해 주는 "prune" 연산을 도입할 수도 있습니다.
코드를 공유하고 싶거나, 다른 사람이 공유한 코드를 보고 싶다면 Unison Share를 살펴보세요.
Unison Codebase Manager의 push 명령을 사용해, 코드베이스를 원격으로 호스팅된 리포지터리에 쓸 수 있습니다.
(언제나 .unison 디렉터리를 통째로 zip으로 묶는 선택지도 있습니다! 그 내용물은 독립적이고 이식 가능합니다.)
상호 재귀 정의 집합은 해싱 관점에서 하나의 "사이클(cycle)"로 취급합니다.
예를 들어:
f x = g (x - 1)
g x = f (x / 2)
먼저, 사이클 안의 재귀 호출을 모두 그 사이클 자체의 De Bruijn 인덱스로 치환합니다(이는 실제 Unison 문법은 아닙니다).
$0 =
f x = $0 (x - 1)
g x = $0 (x / 2)
중첩된 사이클은 더 높은 De Bruijn 인덱스를 갖게 되지만, 최상위 사이클의 인덱스는 0입니다.
이 변환을 통해 사이클의 요소들은 어떤 이름에도 의존하지 않게 됩니다. 그런 다음 이 새 구조의 각 요소를 개별적으로 해싱합니다. 예를 들어 f의 해시가 #222, g의 해시가 #111이라고 합시다. 이후 이들을 정렬해서, 소스 코드 상의 순서와 무관한 정준(canonical) 순서를 얻습니다. 그러면 대략 다음과 같이 됩니다.
$0 =
$0.0 x = #111
$0.1 x = #222
이 구조 전체를 다시 해싱합니다. 이 해시가 #ccc라고 합시다. 그런 다음 각 정의는, 사이클에서의 위치 n에 따라 #ccc.n 해시를 갖게 됩니다. 여기서 g는 #ccc.0, f는 #ccc.1을 갖습니다. 최종 사이클은 다음과 같습니다.
#ccc =
$0 x = $1 (x / 2)
$1 x = $0 (x - 1)
이로써 두 개의 새 해시 #ccc.0, #ccc.1이 생깁니다. 이 정의들이 서로를 해시로 참조하는 것이 아니라, 전체 사이클 내의 위치로 참조한다는 점에 주목하세요.
IO를 사용하는 프로그램은 어떻게 실행하나요?ucm에서, run myProgram이라고 입력하면 됩니다. 여기서 myProgram은 타입이 '{IO, Exception} ()인 term입니다.
Unison 프로그램을 실행하는 여러 가지 방법에 대한 문서를 참고하세요.
Unison은 LSP 통합을 제공합니다. 설정 방법은 여기에서 확인할 수 있습니다.
Vim, Atom, VS Code에서 Unison 문법을 지원합니다. 자세한 내용은 에디터 설정 문서를 참고하세요.
코드베이스를 탐색하는 가장 좋은 방법은 UCM-desktop 앱을 사용하는 것입니다. 이 그래픽 인터페이스는 현재 프로젝트와 라이브러리 의존성을 탐색하고 보는 기능을 제공하며, 앞으로는 코드베이스 관리와 Unison 코드 직접 편집 기능도 포함하도록 확장될 예정입니다.
Unison 코드베이스는 전체 타입 정보와 메타데이터를 포함하는 구조화된 객체이기 때문에, 오늘날의 프로그래밍 언어들에 비해 훨씬 뛰어난 개발자 도구를 만들 수 있습니다!
아직은 아닙니다! 현재로서는 Haskell로 작성된 깔끔하고 단순한 인터프리터를 사용하고 있습니다.
네! 꼭 참여해 주세요 😊
첫 번째 단계는 언어를 가지고 놀아 보면서 Unison 코드를 작성하는 데 익숙해지는 것입니다. 또한 Discord에 참여해 보시고, 이슈 트래커를 둘러보며 어떤 일들이 진행 중인지 살펴보세요.
Unison Share에서 라이브러리를 클론하고, 기능이나 변경 사항을 담은 브랜치를 만들어 Unison 생태계에 직접 기여할 수 있습니다. 라이브러리 작성자는 여러분의 변경 사항에 대한 알림을 받고, 이를 자신의 라이브러리에 병합할 수 있습니다.
Unison으로 작성할 수 있는 라이브러리가 떠오르나요? 이는 컴파일러 팀과 거의 조율할 필요가 없으면서도, 전체 Unison 생태계의 사용성을 크게 향상시킬 수 있는 기여 방식입니다. Unison Share 첫 페이지에는 라이브러리 카탈로그가 있습니다. 여러분의 라이브러리도 꼭 추가해 주세요! 무엇을 작업 중인지 Discord의 #libraries 채널에서 알려 주세요. 😎
또한 base 라이브러리의 누락된 부분을 채워 넣는 것도 좋은 방법입니다. 가장 환영받을 기여 항목은 @unison/base 라이브러리 티켓을 참고하세요.
작은 부분부터 기여를 시작해 볼 수 있습니다.
첫 PR이 머지되고, 작업 방식에 익숙해졌다면, 더 큰 프로젝트에 도전해 볼지 생각해 보세요! 흥미로운 일거리가 아주 많습니다. Discord에서 Paul Chiusano에게 연락해, 관심 분야와 경험 수준에 대해 알려 주세요. 스스로 아이디어가 있다면, 무엇을 하고 싶은지도 함께 알려 주세요!