Unison 애플리케이션과 라이브러리를 작성하기 위한 안내와, Unison 언어의 기본기를 빠르게 훑어볼 수 있는 예제 모음입니다.
URL: https://www.unison-lang.org/docs/
Unison 애플리케이션과 라이브러리를 작성하고 싶다면, 여기 오신 것이 맞습니다. Unison 언어와 생태계는 코드를 즐겁게 작성할 수 있도록 세심하게 설계되었습니다.
Unison 코드가 어떤 모습인지 감을 잡고 싶다면, 아래에 일상적으로 자주 쓰는 언어 기본 요소 몇 가지와 더 포괄적인 문서로 이어지는 링크를 준비해 두었습니다.
이 문서는 설명은 최소화하고 코드 조각 위주로 구성되어 있습니다. 곳곳에 더 자세한 문서 섹션으로 가는 링크가 제공됩니다. 아직 UCM을 다운로드하지 않았다면, 먼저 그걸 해두는 게 좋을 수 있어요. 😎
Unison 코드는 어떤 .u 확장자를 가진 "스크래치" 파일에도 작성할 수 있습니다.
고전적인 helloWorld 프로그램은 IO 능력(ability)을 통해 콘솔 상호작용을 수행합니다. Unison에서 ability가 어떻게 효과(effect)를 모델링하는지 읽어보세요.
unisonhelloWorld : '{IO, Exception} () helloWorld = do printLine "Hello World"
프로그램의 엔트리 포인트는 UCM의 run 명령으로 실행합니다.
textscratch/main> project.create hello-world hello-world/main> run helloWorld
또는 update를 입력해 helloWorld 프로그램을 코드베이스에 추가한 다음, 바이너리 실행 파일로 패키징하여 실행할 수도 있습니다.
texthello-world/main> update hello-world/main> compile helloWorld helloFile
그러면 코드베이스와 같은 디렉터리에 helloFile.uc라는 실행 파일이 생성됩니다. 이후 터미널에서 프로그램을 시작할 수 있습니다.
$ ucm run.compiled helloFile.uc
아래는 매개변수 1개를 받는 함수 double을 소개합니다. 함수 정의에 대한 Unison 관례는 여기에서 자세히 설명합니다.
unisondouble : Nat -> Nat double x = x * 2
> double 4
스크래치 파일에서 >는 watch expression으로 double 함수를 실행합니다.
📚 Unison 투어에서는 watch expression과 다른 워크플로 기능을 안내합니다
Unison에서 계산이 지연됨을 나타내는 방법은 몇 가지가 있습니다: do, 언더스코어 인자, 그리고 ' 기호.
unisonmain : '{IO, Exception} () main = do printLine "hello world" main : '{IO, Exception} () main _ = printLine "hello world" main : '{IO, Exception} () main = '(printLine "hello world")
지연 계산 앞에 !를 붙이거나, 뒤에 ()를 붙여 호출합니다.
unisongreet : '{IO, Exception} () greet = do name = console.readLine() printLine ("Hello " ++ name) greet : '{IO, Exception} () greet = do name = !console.readLine printLine ("Hello " ++ name)
Unison에는 Text를 분할(splitting)하고 검색(searching)하는 함수가 여럿 있습니다.
더 유연한 정규식(regex) 스타일의 텍스트 패턴 작업에는 Pattern을 사용하세요.
대괄호는 Unison 리스트를 도입합니다.
List.++는 리스트 연결(concatenation) 연산자입니다.
리스트는 다양한 패턴 매칭 옵션을 지원합니다.
|> 연산자는 왼쪽 표현식을 실행한 결과를 오른쪽 함수의 인자로 전달하는 "파이프(pipe)"입니다.
괄호로 감싼 x -> x * 100은 List.map에 전달되는 인자로, Unison의 람다(lambda) 문법의 예입니다.
아래 표현식은 if then else 문법과 패턴 매칭 문법 두 방식으로 작성되어 있습니다.
unisonisEven num = if mod num 2 === 0 then "even" else "odd" isEven num = match num with n | mod n 2 === 0 -> "even" _ -> "odd"
Unison의 패턴 매칭 기능에는 변수 바인딩, 패턴 가드(|로 구분), 그리고 as-패턴(@로 표기)이 포함됩니다.
unisonmatch Some 12 with Optional.None -> "none" Some n| Nat.isEven n -> "n is a variable and | is a pattern guard" opt@(Some n) -> "opt binds to the entire optional value"⧨"n is a variable and | is a pattern guard"
cases 문법은 전체 match ... with 표현식을 대체할 수 있습니다.
unisonfoo n = match n with 0 -> "zero" _ -> "not zero" foo = cases 0 -> "zero" _ -> "not zero"
이름으로 유일성이 결정되는 Unison 데이터 타입:
unisontype LivingThings = Animal | Plant | Fungi | Protists | Monera
단일 타입 매개변수를 갖는 재귀적 Tree 데이터 타입:
unisonstructural type Tree a = Empty | Node a (Tree a) (Tree a)
structural 키워드는 같은 구조로 정의된 타입들이 동일하다는 뜻입니다.
데이터 타입과 structural 타입/unique 타입의 차이에 대해 더 알아보기
레코드 타입(record type)은 타입의 필드에 이름을 붙일 수 있게 해줍니다.
unisontype Pet = { age : Nat, species : Text, foodPreferences : [Text] }
레코드 타입을 생성하면 데이터 타입의 필드에 접근하고 업데이트하기 위한 여러 헬퍼 메서드가 함께 생성됩니다.
textscratch/main> add Pet ⍟ I've added these definitions: unique type Pet Pet.age : Pet -> Nat Pet.age.modify : (Nat ->{g} Nat) -> Pet ->{g} Pet Pet.age.set : Nat -> Pet -> Pet
예외는 Exception ability로 "발생(raise)"시키고, 핸들러로 "잡아(catch)" 처리합니다.
📚 ability로 하는 에러 처리 문서에서 이 패턴과 다른 패턴들을 더 자세히 설명합니다
ability는 Unison에서 효과를 관리하기 위해 사용됩니다.
아래는 Random ability를 사용하는 함수인 natIn으로 리스트에서 인덱스로 임의의 원소를 뽑아옵니다. 인덱스가 리스트에 없으면 Abort ability를 사용해 실행을 중단합니다.
splitmix와 toOptional!은 ability 핸들러의 예입니다.
Unison에서는 특별한 문법이나 외부 프레임워크 없이도 Remote ability를 통해 언어 자체에서 분산 계산을 표현할 수 있습니다.
unisonforkedTasks : '{Remote} Nat forkedTasks = do task1 = Remote.fork here! do 1 + 1 task2 = Remote.fork here! do 2 + 2 Remote.await task1 + Remote.await task2
이 예제는 두 개의 계산을 원격으로 실행하도록 fork한 뒤, 결과를 await하여 결합합니다.
📚 더 완성도 높은 분산 사용 사례는 이 글에서 자세히 다룹니다
lib.install 명령으로 Unison Share에서 라이브러리를 가져옵니다.
myProject/main> lib.install @unison/http
먼저 URI를 파싱한 다음, Http.get에 전달하여 HTTP 응답을 가져옵니다. 요청은 Http 핸들러에 전달함으로써 실행됩니다.
readFileUtf8 : FilePath ->{IO, Exception} TextFilePath.writeFile : FilePath -> Bytes ->{IO, Exception} ()renameFile : FilePath -> FilePath ->{IO, Exception} ()
표준 라이브러리에는 유용한 File 작업이 다수 내장되어 있습니다. 이들은 FilePath 및 Handle 네임스페이스 아래에 있습니다.
MVar, TVar, STM 같은 동시성 기본 요소는 베이스 라이브러리에 내장되어 있습니다. TVar와 STM은 락 없이(lock-free) 동시 접근 가능한 가변 데이터 구조를 쉽게 작성할 수 있게 해줍니다. 예를 들어 아래는 간단한 락-프리 큐 구현과 몇 가지 헬퍼 함수입니다:
type STM.TQueue a = TQueue (TVar [a]) (TVar Nat)
아래의 STM.atomically가 도입하는 블록은, 블록 안의 액션들이 수행될 때까지 큐의 상태에 누구도 접근할 수 없도록 보장합니다.