ReScript
2023. 2. 1. 11:37ㆍ개발/실무용 메모
ReScript
개요
- 공식 홈 : https://rescript-lang.org/
- ReScript 란?
- Reason + BuckleScript ⇒ Rescript
- Ocaml 을 기반으로 만든 함수형 프로그래밍언어 Reason 과, 이를 Javascript 로 변환해주는 컴파일러 BuckleScript 를 합쳐 만든 언어
- Javascript의 서브셋 언어이며, 확장자로 .res 사용
- Reason + BuckleScript ⇒ Rescript
ReScript 부트캠프
개발 환경 구축
- rescript 프로젝트 실행
- .res 파일에서 자동 추전하는 플러그인 설치
- 패키지 받기 : npm install 또는 yarn install
- yarn 미설치인 경우
- npm install -g yarn 실행
- yarn -v 버전확인
- Win에서 about_execution_policies 오류 발생시,
- 관리자 권한으로 PowerShell에서 Set-ExecutionPolicy RemoteSigned 명령입력
- 참고 : https://velog.io/@soyi47/yarn스크립트실행오류
- Win에서 about_execution_policies 오류 발생시,
- yarn 미설치인 경우
- 빌드 : npm run build 또는 npm run watch
- bs.js 파일이 새로 빌드되지 않는 경우
- bsconfig.json 기존 코드 아래 추가
"suffix": ".bs.js", "package-specs": [ { "module": "commonjs", "in-source": true } ]
- bs.js 파일이 새로 빌드되지 않는 경우
- 실행 : node “빌드된 파일명”, npm run dev “빌드된 파일명" or yarn dev “빌드된 파일명”
- ex) node .\src\Week1\Year2020Day3.bs.js
- dev 명령어의 경우 nodemon을 사용하여 코드 수정 후 저장 시 자동으로 서버를 restart하여 바로 확인가능
- window일 경우
- wsl 설치 전
- tailwind 문법(%tw("max-w-2xl m-auto flex-col relative")) 을사용하기 위해 "@greenlabs/res-tailwindcss": "^0.1.6" 설치하면 External preprocessor does not produce a valid file에러 발생 + 잡다한 문제
- wsl 설치 후 작업하는걸 추천
- wsl과 nvm 설치
- 홈페이지 참고하여 nvm, node.js 및 npm 설치 항목까지 진행 (노드 버전은 v14.19.0)
- wsl에 명령어 입력
- nvm alias default v14.19.0 // (노드 기본 사용버전 설정) npm install -g yarn // (yarn 설치)
- 홈페이지 참고하여 nvm, node.js 및 npm 설치 항목까지 진행 (노드 버전은 v14.19.0)
- 프로젝트 실행
- wsl에서 워크스페이스 만들기
- 명령어
- cd // ~ 위치로 이동 mkdir (적당한 워크스페이스 이름) 디렉토리로 이동후 git clone (원격지 주소) 프로젝트 폴더로 이동후 code . *참고 /mnt/c/ 위치에서 vs code실행하면 권한 문제 생김
- WSL:Ubuntu로 실행 확인
- 예시
- WSL:Ubuntu로 실행된후 rescript 익스텐션 다시 설치(최초 실행 시)
- 예시
- 모듈 설치 및 앱 실행
- 명령어
- yarn install // 리스크립트 watch옵션 yarn res:start // next 실행 yarn dev
- wsl에서 워크스페이스 만들기
- wsl 설치 전
부트 캠프 진행
ReScript 부트캠프 (Rescript + React + Next)
- 참고 외부 링크
1일차
예습 & 학습 목표
- 시작전 참고사항
- 사전 준비
- ReScript뿐 아니라, React+Next 기술도 포함해서 부트캠프 진행 예정
- 부트캠프는 1월 26일(수)부터 시작하여, 약 3주간 진행 예정
- 첫 1주일은 풀타임으로 진행하고, 이후는 업무 병행 가능성 있음
- 부트캠프 첫주는 ReScript부터 시작
- React도 범위에 포함된 만큼, 부트캠프 전에 배울 수 있도록 기본 개념이나 설치 준비 필요
학습 진행 과정
- Aoc 2020 Day3 문제를 각자 풀어보기
- Rescript 로 바로 작성해봐도 되고 (Mutation 기능도 사용가능), Javascript 로 작성해도 됨
- Day3 문제를 각자 돌아가면서 VSCode 라이브쉐어를 통해 코드 리뷰 및 리팩토링 진행
복습 & 정리
- 기존에 사용하던 명령적 프로그래밍 사고방식에서 선언적 프로그래밍 사고방식으로 전환하는 자세 필요
- 함수형 프로그래밍이 선언적 프로그래밍에 포함됨
- 명령적 방식: ‘어떻게’를 정의
- 선언적 방식: ‘무엇을’을 정의
- 예시
숫자 배열을 받아서, 해당 배열의 모든 원소들을 두 배 시킨 새로운 배열을 리턴하는 'double'이라는 이름의 함수를 작성
- immutable 한 코드 작성
- 상태를 저장하는 변수를 회피하는 방법 (ex: 자바스크립트 let)
- Map,Reduce 등을 이용
- 재귀함수 사용
- 배열 사용
- 자바스크립트
import { readFileSync } from 'fs'; const text = readFileSync("../../input/Week1/Year2020Day3.sample.txt", 'utf-8'); const map = text.split("\r\n") ...#...#..##.# .#.#...#...#. .#.###..##..#. .#..##.##.##.. const x = 0; const y = 0; let countTrees = 0; const bottom = map.length while (true) { x += 3 y += 1 if (y >= bottom) { break; } if (map[y][x % map[y].length] === "#") { countTrees++ } } console.log(countTrees)
- 리스크립트
- 상태를 저장하는 변수를 회피하는 방법 (ex: 자바스크립트 let)
let input = Node.Fs.readFileAsUtf8Sync("../../input/Week1/Year2020Day3.sample.txt")
let map = Js.String2.split(input, "\r\n")
let bottom = map->Belt.Array.size
let countTrees = ref(0)
let rec recFunction = (x: int, y: int) => {
if y <= bottom {
recFunction(x + 3, y + 1)
if y <= bottom - 1 && Js.String2.charAt(map[y], mod(x, map[y]->Js.String2.length)) === "#" {
//map[y][x % map[y].length] === "#"
countTrees := countTrees.contents + 1
//Belt.Array.push
}
}
}
recFunction(0, 0)
Js.log(countTrees.contents)
- 리스크립트2
- let input = Node.Fs.readFileAsUtf8Sync("../../input/Week1/Year2020Day3.sample.txt") let inputArray = Js.String2.split(input, "\\\\r\\\\n") type slopes = { right: int, down: int, } let slope: slopes = { right: 3, down: 2, } type right = {mutable newRight: int} let makeCoords2 = (slope, inputArray) => { let end = inputArray->Belt.Array.length - 1 let rec go = (acc, newRight, i) => { if i > end { acc } else { let right = mod(newRight + slope.right, Js.String2.length(inputArray\[i\])) let newAcc = acc->Belt.Array.concat(\[(right, i)\]) go(newAcc, right, i + slope.down) } } go(\[\], 0, slope.down) } makeCoords2(slope, inputArray)->Js.log
- 커링(currying)
- 리스크립트의 함수는 기본적으로 커리드 함수
- 다중 인수를 갖는 함수를 단일 함수를 갖는 함수들의 함수열로 바꾸는 것을 뜻함(함수가 함수를 리턴)
- 예시
- const sum1 = (a) => { return (b) => { return a + b; } } ==================================== const sum = (a, b) => a + b; sum(1, 2); // 3 ‘다중 인수’ 함수 코드를 ================================= const sum1 = a => b => a + b; sum(1)(2); // 3 ‘단일 함수 함수열’로 변경한다는 뜻
- 사용이유
- 재사용성
- 함수의 실행을 원하는 지점까지 미룰 수 있음
- 예시
- const sum = a => b => a + b; const addThree = sum(3); // sum 함수는 실행되지 않는다. addThree(4); ================================================== sum(3)(4)를 해주는 것이 아닌 sum(3)만 해준 다음 남은 함수인자(4)를 기다리게 할 수 있다.
2일차
예습 & 학습 목표
- Aoc 2020 Day3 문제를 자바스크립트 기준 let 대신 const 를 사용하여 작성 해보기
- 하나의 함수로 작성했던 부분들을 잘게 쪼개서 여러개의 함수로 나누어 보기
- 이러한 방법으로 Day3 문제를 풀고나면, 리스크립트로 다시 작성해보거나 Day5 문제 풀어보기
학습 진행 과정
- 1일차와 마찬가지로 각자 풀어온 Day5 문제를 코드리뷰후, 리팩토링 진행
복습 & 정리
- (1일차와 마찬가지로) 사고방식을 변경하는 자세가 필요
- 복잡한 문제를 복잡하게 풀 필요 없음
- 주어진 전체 문제를 보다 작은 단위로 생각해보기
- 주어진 입력에 대해서만 생각 하지 말고, 작은 단위로 나누게 되면 각각의 역할에 대해서 생각하게됨
- 이렇게 구현 하면 모듈화 / 재사용성이 높아짐
- 작업들이 서로 섞이지 않고 격리되도록 고려
- 각각의 역할에 대해 생각하게 된다는 뜻은, 예를들어, 문자를 입력받아 쪼개진 문자들이 담긴 배열을 만들어주는 역할, 배열을 돌면서 계산을 하는 역할 등 세부적인 단위로 함수를 나누어 보는것을 뜻함
- 함수형 프로그래밍 관점
- Map 의 역할, 중요성. (참고 : Promise 의 then 과 유사)
- 기타 참고 사항
- 파이프 오퍼레이터 알아보기
3일차
예습 & 학습 목표
- Aoc 2020 Day5 문제
복습 & 정리
- 자바스크립트에 있는 함수 사용하는 방법
- external parseInt(리스크립트에서 사용할 이름): (string, int) => int "parseInt(자바스크립트에서 사용할 함수)"
- option 타입
- null을 명시적으로 사용하기 위한 타입 = rescript에서 null, undefined를 표현하기위해 사용
- None | Some('a) -> Belt.Option.getWithDefault를 사용해서 패턴매칭을 대체 가능
- 패턴 매칭
- let intRow = switch row { | Some(n) => n | None => 0 } => Belt.Option.getwithDefault(0) : 위 코드와 같음
- 타입을 쪼개서 생각해보기
4일차
예습 & 학습 목표
- Aoc 2020 Day6 문제를 어떻게 풀지 알고리즘을 문장으로 작성해보기
복습 & 정리
- 함수형 프로그래밍 관점
- 주어진 문제를 탑다운 방식으로 보는 경향보다, 더 세부적인 단위로 파악해보기
- 예를들어 day6번문제를 기준으로
- 개행제거하여 배열로 만드는 함수
- 만들어진 배열을, 한번 더 각 문자별로 쪼개서 이중배열 구조를 만드는 함수
- 중복을 제거하여 하나의 배열로 합쳐주는 함수
- 함수의 길이를 구해 반환해주는 함수등...
- 예를들어 day6번문제를 기준으로
- 자바스크립트의 flatmap -> 리스크립트 map/concatMany 변환
- flatmap → flat과 map 함수를 합친 역할을 수행해주는 함수
- rescript 에서는 존재하지 않아 flat 역할을 할 함수와 map함수를 조합하여 구현해야함
- 재사용 가능하도록 코드를 구성
- 예를 들어 인자로 function을 전달해주는 것 만으로도 기존 구현코드를 재활용하여 구현할 수 있음
- const program = (*input*, *f*) => sum(allToGroup(*input*).map(*group* => f(*group*)))
- 예를 들어 인자로 function을 전달해주는 것 만으로도 기존 구현코드를 재활용하여 구현할 수 있음
- 참고 사항
- flatMap이 array에만 있지 않고 option이나, promise에도 있음
5일차
예습 & 학습 목표
- Aoc 2020 Day6 문제 part2 를 Belt.Set.String의 intersect와 union을 사용하여 풀어보기
- Aoc 2020 Day2 문제 풀어보기
복습 & 정리
- 함수형 프로그래밍
- 관심사를 분리하여 독립적인 단위로 코드를 작성해보기
- ex) map 내부를 따로 함수로 빼기
- map형태의 함수를 사용함으로써 핵심로직에 해당하는 함수와 부수효과인 map함수를통해 각각을 분리하고, 핵심로직의 함수를 좀 더 범용성있는 형태로 사용할 수 있음
- 관심사를 분리하여 독립적인 단위로 코드를 작성해보기
- 항등원의 개념. 연산자와 집합에 따라 달라짐
- 더하기는 0 곱하기는 1
- 합집합은 공집합
- 교집합은 전체집합
- 리듀스를 사용할때
- 초기값이 무엇인지, 초기값을 가지고 어떤식으로 연산해 나갈것인지 고려하기
- 참고 사항 및 키워드
- Tacit programming
- 모노이드 : 항등원을 갖는 결합법칙을 따르는 이항 연산을 갖춘 대수 구조
- 유니온 vs 태그드유니온
- Union Type : 타입을 여러 개 연결하는 방식
- ex) 자바스크립트 or 연산자
- Tagged Union Type : 겹치지 않는 타입으로 이루어진 유니온 타입
- Union Type : 타입을 여러 개 연결하는 방식
6일차
예습 & 학습 목표
- Aoc 2020 Day2 문제 풀어보기
- Aoc 2020 Day2, Aoc 2020 Day4 문제는 '파싱' 이 핵심 키워드
- 파싱을 하는 작업은 구조가 계속 유지되어야 한다는 관점으로 해야함
- 파싱을 하는 것은 실패할수도 있다는것을 고려
- 옵션(Option)을 계속 유지하는 방향으로 작성
- 리턴될때 Option과 같은 형태로 리턴되는게 좋음
- Option타입은 Some 이나 None 을 가짐
- 실패할 가능성이 있는 작업은 option 또는 result타입을 리턴 (부트캠프에서는 option주로 다룸)
- Belt.Option.map
- Some일때만 작업을 수행, None은 그대로 None 작업수행하지 않음
복습 & 정리
- parsing
- 파싱을 하는 작업은 구조화 되어있지 않은 데이터를 구조화 하는 작업
- 파싱을 하는 것은 실패할수도 있다는것을 고려
- flatMap
- Option의 결과가 나오는 map 대신 flatMap을 사용하면 Option 를 얻을 수 있음
- 리스크립트에서의 리팩토링
- 타입스크립트 보다 세부적인 컴파일러의 역할로 인해 리팩토링 작업 시 컴파일러의 에러만 따라 수정하여도 매우 효과적으로 수정가능
7일차
예습 & 학습 목표
- Aoc 2020 Day4 문제 풀어보기
- Aoc 2020 Day4 문제는 이번 부트캠프의 주요한 핵심 문제
복습 & 정리
- Aoc 2020 Day4 문제 리팩토링
- 타입을 좀 더 범위를 좁혀 사용해보기
- 타입 선언 시 특정 값이 꼭 포함되어야하는 타입인 경우 배리언트를 통해 별도의 타입선언을 하여 사용가능
- 예) ecl: string -> ecl: ecl. type ecl = Amb | Blu | ...
- 타입을 좀 더 범위를 좁혀 사용해보기
- 패턴매칭과 배리언트는 밀접한 관계를 가지고 있음
- 잘 다루면 상태관리 할 때 도움됨.
- 타입을 선언하여 사용 할 때, 한번에 완전한 형태로 선언하여 사용하지 않고, 중간단계에서 불완전한 타입을 사용해보는것도 하나의 방법
- Next.JS 환경에서 리스크립트를 사용하는것이 부트캠프 최종목표
- keep은 Js의 filter와 동일한기능
- 참고 키워드
- 리스크립트에서 Json 파싱
8일차
예습 & 학습 목표
- Aoc 2020 Day4 의 파트2 문제 리팩토링 해보기
- Next + Rescript 개발 환경 구축해보기
- 템플릿을 이용하여 만들어도 되지만, 구조나 설정등을 확인 해보기
- 해당 개발 환경을 이용하여, 팜모닝 패밀리 서비스(B2C) 개발 환경 구축 가이드 작성 예정
복습 & 정리
- Aoc 2020 Day4 파트2 문제 리팩토링
- 파싱한 데이터는 검증이 끝났기 때문에 추가 검증이 필요없음
- validation 과 parse 의 차이
- validation은 값을 검사하여 true false 로 리턴
- parse는 파싱에 성공하면 값을 그대로 리턴
- 참고 키워드
- 시퀀스 (promise.all 도 시퀀스 함수의 일종)
- 리스크립트에서 next나 radixui 같은 외부 라이브러리를 사용하기 위해 바인딩처리를 어떤식으로 하였는지 검토해보기
9일차
예습 & 학습 목표
- tailwind ppx를 설치하고 %twc를 사용하여 구축한 개발환경의 클래스네임들에 적용해보기
복습 & 정리
- Next 환경에서 라우팅 처리 해보기
- Link 태그 사용
- 라우터의 리다이렉트 기능
- 상태관리
- Redux, localStorage, sessionStorage 사용
- React hook
- useState, useEffect, useReducer
- TailwindCss
- 스타일을 좀 더 규칙화 해서 사용하도록 하는 것을 지향
- Swr
- Api 호출 뿐 아니라 fetcher 설정을 통한 타겟 변경으로 localStorage, sessionStroage 도 관리가능
10일차
복습 & 정리
- SWR 사용해보기
- 데이터를 가져올때 데이터를 가져오는 어떠한 라이브러리도 사용 가능
- fecth, axios, GraphQL
- GraphQL -> 선언적 데이터 패칭
- key 와 fetcher를 받음
- API를 call 하는 기능은 존재하지 않음
- fetcher 함수를 통해 요구하는 동작을 수행함 (axios, graphql 등)
- 데이터를 가져올때 데이터를 가져오는 어떠한 라이브러리도 사용 가능
- useEffect
- 리스크립트는 가변배열을 사용 할 수 없어서 개수가 정해진것만 전달가능
- useEffect0 1 2 3 .. 방식으로 사용
- useEffect를 써야하는 상황은 구조가 잘못되었을 가능성 ㄱㅓㅁ토
- 리스크립트는 가변배열을 사용 할 수 없어서 개수가 정해진것만 전달가능
- 상태관리
- 전역상태가 과연 꼭 필요한지 생각해보고, useEffect 남발하지 않기.
- 리스크립트에서는 useEffect0, 1, 2.. 로 나뉨
- 플럭스 아키텍처 개념 파악해보기
- 리덕스, 리코일등..
- 앞으로...
- Vercel을 이용해 개인별로 배포를 진행해보기
- 협동 프로젝트로 부트캠프에서 배운내용 적용해보기
소감
- 함수형 언어(Rescript) 개발 소감
- 작은 단위의 함수 재사용 장점
- 불변성+고차 함수 -> 생각(설계)방식 전환
- 유익했지만, 생각(설계)방식이 어려움
- 리스크립트 능숙해지려면 훨씬 많은 시간 필요할 듯 (특히 type)
- 프론트엔드 기술 스택 소감
- RS vs. TS(JS)의 장단점에서 특이 바인딩 단점을 뛰어 넘는 정도의 장점이 있을까
- 함수형 적용은 좋지만 TS로 하면 어떨지
- Win에서 구축 어려움
- 부트캠프의 교육 방식 소감
- 혼자 공부할때보다 훨씬 나음 -> 하루 단위 과제 및 피드백
- 1:1에 최적화된 방식 같음 (코드를 다 봐줄순없으니까)
- 함수형 언어 및 기본에 해당하는 것은 다 스스로 해야하고 가이드가 없던 점이 아쉬움
ReScript 개념 이해
JS/TS 와 주요 차이점
- 클래스 → 플레인 데이터 + 함수
- if문과 가상 디스패치 → 패턴 매칭
- 문자열 오남용 → 데이터 모델링(Variant)
- 마이그레이션 : 넓이 우선 → 깊이 우선
주요 특징
- Javascript의 서브셋
- 사용하기 더 쉬움
- 빌드 최적화 (Just-In-Time 최적화)
- 사용하지 않는 코드 제거가 강력함
- 용량이 작은 Javascript 결과물
- 빌드 시간이 짧음
- 빌드 결과 Javascript의 가독성 고려
- 한 Rescript 파일(.res) 당 한 Javscript 파일 생성 → 기존 도구 통합을 쉽게 해줌
- 강력하고 빠른 타입 시스템
- 타입 추론 시스템이 명확함
let add = (a, b) => {
a++b
}
add("1", "2")
This expression has type string but an expression was expected of type int
// Typescript 에서는 no-implicit-any 에러. 암시적으로 선언되었는데 추론 타입이 any 일때
// Rescirpt의 추론 과정
// Rescript 에서 + 오퍼레이터는 int 형에만 사용할 수 있음.
// 따라서 구문 내의 a , b 는 int 형임을 추론하고,
// Rescript 에서는 모든 식별자가 불변형이므로 파라미터로 받은 a , b 도 불변.
// 그러므로 a , b 는 중간에 다른값으로 변경 될 수가 없음을 추론하고,
// a , b 는 int 타입임을 보장함 -> 추론 기반 : 힌들리-밀러 타입시스템
-
- 타입 정의나 조정이 필요 없음
- 타입 함정이 없음 (Soundness)
- null/undefined 에러가 없음
- 타입 어노테이션이 필요 없음
- JS 직접 사용도 가능
언어적 특징
- 문장 끝 ; 없음
- null, undefined 없음
- import 구문이 필요 없음
- 모든 Rescript 파일 = 모듈이므로, 다른 파일에 접근 = 다른 모듈에 접근
- 이러한 이유로 파일 이름이 중복되면 안됨
- 기본적으로 모든 파일(모듈)은 exported 상태
- 변수
- JS const x = 5; → RS let x = 5
- JS let x = 5; x = x + 1; → RS let x = ref(5); x := x.contents + 1
- let 이 기본적으로 불변이며, ref()로 수정 가능하게 만들 수 도 있음
- 문자열
- 문자열 표현은 “” 만 사용, ‘’ 사용 불가
- 문자열 합칠 때 + 대신 ++ 사용
- ex) "hello" + "world" → "hello" ++ "world"
- `` 와 ${} 사용 동일
- ex) hello ${message}
- 객체/레코드
- JS 타입 없음 → RS type type point = {x: int, mutable y: int}
- 그 외 객체 표현 같음
- 배열
- [1, 2, 3] , myArray[1] = 10 등 표현 방식 동일
- 다른 타입이 한 배열에 섞일 수 없음
- 함수
- arg => retVal 표현 동일
- add(4, add(5, 6)) 표현 동일
- function 키워드 없음, arrow 함수가 기본
- function named(arg) {...} → let named = (arg) => {...}
- const f = function(arg) {...} → let f = (arg) => {...}
- return 키워드 없음, 스코프 내의 암시적 리턴
- 조건문 : if-else, switch
- 조건문에 괄호가 없고, 표현식 사용
- if (a) {b} else {c} → if a {b} else {c}
- a ? b : c 표현 동일
- switch 표현도 괄호 없이 동일
- 조건문에 괄호가 없고, 표현식 사용
- 반복문 : for, while
- for (let i = 0; i <= 10; i++) {...} → for i in 0 to 10 {...}
- for (let i = 10; i >= 0; i--) {...} → for i in 10 downto 0 {...}
- while (true) {...} → while true {...}
- 블록 스코프
- { ... } 암시적으로 가장 마지막줄이 반환됨
- 리스크립트 if, while 구문과 함수도 같은 블록 스코프 방식
- 예외 처리
- throw new SomeError(...) → raise(SomeError(...))
- try {a} catch (Err) {...} finally {...} → try a catch { | Err => ...}
- finally 없음
- 배리언트 (Variant) 선호
- 배리언트 자료구조는 타입에 대해서 'a 또는 b' 형태를 취할 수 있게 해주는 문법
- type myResponse = | Yes | No | PrettyMuch let areYouCrushingIt = Yes // myRsponse 타입은 각각 Yes No PrettyMuch 라는 배리언트 생성자를 가지고 있고, // 이는 | (bar) 로 구분하며, areYouCrushingIt 의 타입은 Yes
- 배리언트 생성자는 추가 인자를 가질수 있음
- type account = | None | Instagram(string) | Facebook(string, int) let myAccount = Facebook("harold", 21) let yourAccount = Instagram("Gunners")
- 배리언트는 성능적인 측면에서 속도를 많이 향상 시킬 수 있음
type animal = Dog | Cat | Bird let data = Dog switch data { | Dog => Js.log("Wof") | Cat => Js.log("Meow") | Bird => Js.log("Kashiiin") } // 컴파일러는 이 코드를 읽고, type animal = 0 | 1 | 2 으로 변환한뒤, switch 문을 // 일정한 시간이 소요 되는 O(1) 점프테이블로 변환한다.
- let data = 'dog' if (data === 'dog') { ... } else if (data === 'cat') { ... } else if (data === 'bird') { ... } // 이 코드는 시간복잡도가 O(n) 이다.
- 파이프 연산자 ( → ) 지원
- 기본형태 a(b) 를 b → a 같은 형태로 바꿔 작성 가능.
- Place Holder 기능
- 언더스코어 ( _ ) 를 통해 작성 가능
- let addTo7 = (x) => add3(3, x, 4) let addTo7 = add3(3, _, 4) //두개의 문법이 같음
- JS module 과 Belt module
- JS module
- 기존 javascript api 모듈
- Belt module
- javascript 에서 사용할 수 없는 기능들이 추가된 모듈
- JS module