기록/2019-06-02

나루 위키
둘러보기로 가기 검색하러 가기
  • (lifthrasiir) 연산자 이름을 유지해야 할 필요성을 느끼지 못하고 있다.
    • 원래 의도는 Python magic method(__add__)나 C++ operator name(operator +)와 유사하면서 좀 더 체계적으로 정해지는 이름(#+# 또는 +#, #가 피연산자 위치)을 원했던 것이었다. 어느 정도는 Coq/Agda 스타일 notation을 따라한 것도 있었다.
    • 그런데 "특정 연산자를 구현하는 타입" 같은 걸 타입 시스템에 포함하기 시작하면 interface든 trait든 뭔가 연산자를 나타내는 타입 상의 주체가 있어야 하는데, 그럼 굳이 특수한 이름이 필요한 게 아니라 그 주체랑 매어 놓으면 되는 거 아니냐? 싶은 것. (예: 러스트는 + 연산자가 std::ops::Add trait의 add method)
    • (Kroisse) 하스켈과 같은 모델은 어떤가? 연산자 생성은 자유롭고, 연산자 자체는 특수한 함수 이름으로 취급되는 것.
      • (lifthrasiir) 그렇게 바꿀 수도 있을 것 같은데 "굳이" 그렇게 해야 하냐면 또 모르겠다. 일단은 연산자 이름이 없다고 가정하고 작업을 해 보도록 하겠다.
  • (Kroisse) 연산자 오버로딩을 하게 되면 multiple dispatch는 어떻게 되는가? 이것도 연산자 이름에 영향을 줄 것 같은데.
    • (lifthrasiir) 가능하면 symmetric multiple dispatch를 하고 싶다. 여기서 symmetricity라는 건 이를테면, 자바에서는 receiver(self)와 다른 인자가 dispatch에 다른 규칙을 적용받고, self와 다른 인자를 뒤집으면 다르게 resolve되는 경우가 있는데, 그렇게 하지 않겠다는 것이다. 그런데 이게 많이 까다롭다; 이렇게 동작하는 언어가 드물다는 것만 봐도 알 수 있다.
    • (lifthrasiir) 일부 언어는 어떤 함수는 선언 순간 확정되고, 어떤 함수는 multiple dispatch가 가능하도록 확장되게 할 수 있다. 대표적으로 Perl 6에서 sub/multi 구분이 있다. 비슷하게 var로 선언된 함수는 고칠 수 있고 그렇지 않은 함수는 고칠 수 없게 할 수 있지 않을까.
      • (lifthrasiir) 이거 써 놓고 나니까 OCaml 4+의 extensible variant와 굉장히 유사한 것 같다. 이게 본래 3.xx대에서는 exception type이라고 variant를 지속적으로 추가할 수 있는(하지만 실행 시간엔 그 목록이 확정되는) 특수 타입이 있었는데, 그걸 일반화한 것.
      • (Kroisse) 그거 자바 checked exception과 비슷한 거 아닌가;;;
      • (lifthrasiir) 함수마다 그 함수가 반환할 수 있는 exception list를 가지고 있는 게 아니므로 다르다. 굳이 그거랑 비교하자면 Zig의 error set type이 비교할만 하다. 개인적으로는 Zig의 이 컨셉은 checked exception의 현대적인 재해석이라고 생각한다.
      • (Kroisse) Swift에서는 한동안 러스트 Option에 대응하는 타입만 존재하다가, 결국 throws를 붙여서 (나루의 실패에 대응하는) 오류를 반환할 수 있는 함수를 만들었다. 그 뒤에 Result에 대응하는 타입도 뒤늦게 추가되어 결과적으로 오류 처리 방법이 3가지...
        • (lifthrasiir) C++ noexcept를 뒤집은 것과 유사한가? (주: 말할 때 실수로 nothrow라고 말했었음)
        • (Kroisse) 그렇게 생각할 수 있겠다. Result와는 달리 반환 타입이 바뀌지 않는다.
        • (lifthrasiir) 일종의 effect system이네;;;
        • (Kroisse) 솔직히 async/await 수준...
    • (lifthrasiir) 비슷한 맥락에서 나루에서는 함수와 메소드 구분이 희박한데, 단적으로 러스트 trait처럼 메소드도 영역scope에 매여 있어서 영역을 들이지 않으면 메소드를 쓸 수 없고 한정된 영역 안에서만 존재하는 기존 타입에 대한 메소드도 만들 수 있어야 한다고 본다.
  • (Kroisse) 적어도 의미론단에서는 모두 복사가 일어나고, 내부적으로 copy-on-write하는 식으로 최적화만 해야 하지 않겠는가?
    • (lifthrasiir) 나도 비슷하게 생각함. 예전 스펙 보면 reference에 대한 내용이 있고 @ 연산자가 존재하는데 이 연산자를 안 쓰면 모두 복사한다는 뜻이다. 대부분의 타입은 복사가 일어나야 한다.
    • (Kroisse) 복사가 일어나면 안되는 타입이 있는가?
    • (lifthrasiir) 뭐 예를 들면 파일 디스크립터 같은 것은... 아 물론 이론적으로는 복제 가능하지만ㅋㅋㅋㅋㅋ 좀 무리수 같기도.
    • (Kroisse) 그러한 것들은 생각해 보면 캡슐에서 관리해야 할테니 굳이 그걸 고려해야 할 필요는 없을 듯?
    • (lifthrasiir) 아 그럼 캡슐은 파일 디스크립터(등)의 핸들만을 유저 코드에 노출시킨다? 그거 그럴듯한 방법 같다. 고려해 보겠다.
  • (ditto) mutable한데 복사가 기본이면 좀 헷갈린다고 생각하긴 한다
    • (lifthrasiir) 원 의도는 @ 연산자를 쓸 때만 변경이 일어난다는 것이었다(변수는 제외). 예를 들어서 x := (a: 1, b: 2, c: 3)이면 x a = 4 같은 건 불가능한 것이고, x := new (...)로 선언되어 있어야만 x@ a = 5 같은 게 가능한 것이다(레코드가 변경 가능하다고 가정할 경우, 이건 아직 결정되지 않음).
    • (lifthrasiir) 망해서 오픈소스가 된(...) 페이스북의 Skip 언어의 mutability 접근이 내 생각과 굉장히 유사하다. http://www.skiplang.com/docs/mutability.html
  • (disjukr) FFI가 어려워지지 않을까?
    • (Kroisse) FFI 자체를 데이터 드리븐으로 만드는 식으로 피해갈 수 있다고 본다. 그렇게 되면 필연적으로 나루 코드와 외부 통신은 직렬화로 일어나게 될 것이고 변경 가능한 값은 오가지 않게 된다. wasm과 유사함
    • (lifthrasiir) 이론적으로는 그러한데, 그런 모델을 쓰면 망하는 경우가 좀 있다(OpenGL 따위). wasm에서도 성능 문제 때문에 direct C FFI를 고려하고 있는 걸로 알고 있는데...
    • (Kroisse) 결국 low-level FFI와 high-level FFI(캡슐을 통하는)가 나뉘지 않을까?
  • (Kroisse) object type 같은 것은 존재할 예정인가?
    • (lifthrasiir) 기본으로는 structural하고, 그런 타입을 nominal하게 만드는 new type 선언이 예정되어 있다. 예를 들어서 Date := new type (year: int, month: int, day: int)
    • (lifthrasiir) 정확한 문법 등은 용례를 보고 고치게 될 것 같지만 어쨌든 이 타입은 본래의 (...) 레코드 타입과 별도가 되어 메소드 등도 따로 존재하게 된다.
  • (Kroisse) 함수 클로저가 바깥의 값(upvalue)을 close하면, var x := ... 따위로 선언된 upvalue도 호출할 때마다 복사가 일어나야 하는가?
    • (lifthrasiir) 사람들이 생각하는 것과 크게 다를 듯. 나는 var x: T := ...는 근본적으로 x: T@ := new ...의 desugaring이라고 본다(그리고 x가 사용되는 모든 곳이 x@로 바뀜).
    • (Kroisse) 이게 문제가 되는 건 개념적으로 값은 복사를 해야 하는데 a := fn { ... }; b := a 했을 때 a와 b가 내부적으로 공유 상태를 들고 있을 수 있기 때문이다.
    • (lifthrasiir) 좋은 지적이다. internal reference와 근본적으로 같은 문제를 공유하는 것 같은데 예상보다 subtle한 듯;;;