naru revision 3.1 planning. last updated on 2008-07-28 ------------------------------------------------------------ ## value - const semantics 관련. 별도의 문법을 빼는 것으로 결론내렸음. 컴파일러는 immutable object를 감지해 낼 수 있어야 한다. mutable object는 frozen(), frozen? 메소드 등으로 immutable이 될 수 있다. (underlying Object 구현에 따름) ------- extend tuple to record! a = 1, 2, 3 -- is tuple b = alpha: 1, beta: 2, gamma: 3 -- is record -- accessing by identifier println a.#0, a.#1, a.#2 println b.#alpha, b.#beta, b.#gamma -- accessing by variable (desugared form) println a.#(0), b.#(:alpha) -- field listing println a.class.fields -- [0, 1, 2] println b.class.fields -- [:alpha, :beta, :gamma] println (42).class.fields -- [0], since (42) is implicit singleton -- further thought: @ 대신 #를 아무 데나 쓰기. -- 이러면 @asdf 같은 게 확실히 internal variable로 쓰일 수 있다! -- (clarify: 결국 operator는 #로 바뀌었음) #+# := fun (a, b, _orig := #+#) println #"a = #a, b = #b" -- or maybe $"a = $a, b = $b"? return _orig(a, b) end class blah self.#[#] := ... def self[value: Integer] ... end end ## expression and statement ------- AST를 내뱉는 construct가 필요함. (meta programming을 위해서 필요...?) -- 문법은 맘에 안 들지만 conceptually 이렇게. -- quote와 !quote는 lisp의 '나 ` 따위를 의미함. ast = quote (3 + !quote (5) + 4) -- quote니까 차라리 이렇게? ast = `3 + (`5`) + 4` - %로 시작하는 다양한 것들 %()는 %Q()와 같음 %q()는 raw string literal %Q()는 interpolating string literal %w()는 array of raw string %W()는 array of interpolating string %p()는 raw pattern literal %P()는 interpolating pattern literal ----- quote summary "asdf" -- normal string r"asdf\nasdf" -- raw string #"asdf #i/#j" -- interpolated string x"40 3D 1F C6" -- hexadecimal-encoded byte sequence b"asdf" -- as-is byte sequence (what encoding?) br"asdf" (or rb"asdf", which is recommended) -- as-is raw byte sequence ?"Zero Width Joiner" -- extended character literal :"you've got mail" -- extended symbol literal """asdf""" -- triple-quoted string r"""...""", #"""...""", b"""...""", br"""...""" -- similar /asdf*/ -- case-sensitive pattern /asdf*/i -- case-insensitive pattern /asdf*/w -- white-space ignoring pattern (but interpolated pattern doesn't ignore spaces) b/asdf*/ -- pattern for byte sequence x/61 73 64 66*/ -- hexadecimal-encoded pattern for byte sequence %{ something} -- general delimited string %r{something} -- general delimited raw string %#{something} -- general delimited interpolating string %w{a b c d e} -- general delimited list of tokens (strings) %p{something}i -- general delimited pattern ** there is no heredoc. :p most case is covered with triple-quote. ----- syntax extension use operator "" "!" (400) -- factorial use operator "<<" ">>" (350) -- <> operator use operator "" "><" "" (800) -- cross operator ([1,2,3] >< [4,5,6]) use operator "" ":->" "??" "" (10) -- ternary operator (a :-> b ?? c) use operator "#q (" "," "," "," ")" (720) -- #q(1,2,3,4) use token "\u2211" use operator "\u2211 (", 40, ")", 80 = 120 OPERATOR EXAMPLE infix/chained: #+#, #*#, #/%#, ... prefix: not#, ... suffix: #with, ... closure: not in standard, e.g. {{#}} prefix closure: not in standard, e.g. [#]# postfix closure: #(#), #[#], ... how about __radd__ and __add__? ===) type checking! a + b is equivalent to, a.class.#+#(a, b) if #+# is of the type (AType, BType) b.class.#+#(a, b) if #+# is of the type (AType, BType) -- if AType == BType, then the order doesn't matter #+#(a, b) otherwise you can use the following type of definitions: #+# := fun (self, rhs: MyType); (-- ... --); end #+# := fun (lhs: MyType, self); (-- ... --); end ------ arbitrary base in number 16435934 0xFACADE 16'facade' 1/16f 0.0625f 2'1'e-14f e.g. if we change exponent syntax to !-5 or !5? avogadro := 6.02214!23 -- changed from 6.02214e23. epsilon = 2'1'!-53f -- or 2'1!-110101'f? (-_-;) ## variable ------ ...의 사용. ------ 함수 prototype을 assignment/declaration에서 일부 쓸 수 있다 a, b, ... = 1, 2, 3, 4, 5, 6 -- a, b: Integer a, b, c... = 1, 2, 3, 4, 5, 6 -- a, b: Integer, c: List a, b: Integer, ... = 1, 2, 3, 4, 5, 6 var a, b: Integer, ... = 1, 2, 3, 4, 5, 6 - =, := 등을 expression으로 둬야 하는가? 그렇게 한다면 inline assignment가 가능해진다. (하지만 그다지...) condition에 as 같은 걸 generic하게 bound해서 쓰게 하면 필요가 없어지는 걸까? -> aliasing을 사용하자? 확장해서 with 같은 local binding construct에도 같은 문법을 쓰자? - 좀 더 확장하면 :=가 lazy calculation을 의미하도록 할 수도 있으나 흠좀... ------ ADL을 도입해야 할까? @+@ := ... namespace naru namespace core add := fun (A, B) return A + B end end end namespace X @+@ := ... test := fun (A, B) return add(A, B) end end namespace Y @+@ := ... test := fun (A, B) return A.test(A, B) end end Y.test(3, 4) -- ???? ## function fun blah -- 아무 인자도 받지 않음. fun blah()도 마찬가지 end fun blah(a, b, c) -- 항상 세 개의 인자를 받음. 형식은 Object임 end fun blah(a: Integer, b: Float, c: [Integer]) -- 형식을 명시적으로 지정함. c: List[Integer]를 쓸 수도 있음. -- (T,U), [T], {T}, {T=>U}, T->U 형태가 가능하다. -- (마지막 문법은 함수로, 함수 인자는 tuple에 가깝기 때문에 -- 하나의 인자가 아닌 이상 항상 괄호가 필요하다!) end fun blah(a: Integer, b?: Float, c: Float = 3.0f * a) -- 이름 뒤에 붙은 ?는 optional의 의미. 기본값은 undef이다. -- * 모든 변수는 undef로 만들 수 있다. 즉, undef는 변수의 값이라기보다는 -- 변수의 "상태"를 나타내는 값이다. -- c의 경우 ?가 없어도 기본값이 있으므로 optional하게 처리된다. -- =를 쓸 경우에는 call-time에 평가되기 때문에 이전에 나온 -- 변수들도 사용할 수 있다. 하지만 :=는 def-time에 평가되므로 불가능. end fun blah(a: Integer, : Float, ?: Decimal, *?: Char): String -- : 앞에 변수를 생략하면 받기는 하지만 사용하진 않는다. -- : 뒤에 공백을 뺄 경우 다른 뜻(심볼)으로 해석되므로, -- 변수 대신에 "*"를 사용하는 것이 권장된다. (형 선언에서도 쓸 수 있음) -- 함수는 또한 반환 형식도 가질 수 있다. end fun blah(a(b: Integer, c: Integer), d[e: Float, f...]) -- 이 경우 a와 d는 자동으로 (b, c)와 [e, f...]로 바인딩된다. -- a와 d에 별도의 형식을 지정할 수도 있으나 서로 일관성이 있어야 함. -- 만약 [:Float, :Integer, ...]식으로 쓰면 적절한 방법으로 -- 모든 값을 propagate하려 하거나 에러를 반환한다. end fun blah(a, b, :option1, :option2) -- 옵션 :option1과 :option2가 설정된 경우를 나타낸다. -- 함수 호출 문법은 blah(3, 4, :option1, :option2) 또는 -- blah(3, 4) with (:option1, :option2)를 사용한다. -- confliction resolution: -- blah(a, b, c: Symbol, :option2) -- blah(a, b, :option1, :option2) -- 두 개가 같이 있을 경우 blah(3, 4, :option1, :option2)는? -- 이 경우에는 후자를 사용한다. (두 인자가 바뀔 경우를 생각하자) end fun blah(a, b, :option1?, !:option2) -- :option1은 있을 수도 있고 없을 수도 있다. -- :option2는 없어야 한다. (앞의 !는 not의 의미) -- :option1의 여부는 $:option1 변수(Boolean)를 사용한다. end fun blah(a, b, c..., ^d = 3, ^e, ^f...) -- #TODO: (a, b, c...; d = 3, e, f...)를 쓸 수도 있음 -- c는 모든 이름 없는 인자들을 리스트로 모은다. -- ... 뒤에 형식을 지정하지 않으면 List[Object] 형을 사용한다. -- ^가 붙은 인자는 이름이 붙은 인자를 의미한다: -- d는 생략 가능하지만 무조건 이름 있는 인자로 들어 가야 한다. -- e는 생략이 불가능하며 무조건 이름 있는 인자로 들어 가야 한다. -- f는 어디에도 속하지 않은 모든 이름 있는 인자를 연관 배열로 -- 모으며, 형식을 지정하지 않으면 Map[Symbol, Object] 형이 된다. -- (정확히 말하면, List[Tuple[Symbol, Object]]로부터 adopt 가능) end fun blah(a: Integer <- *, b: Integer <- Object) -- "<-"는 (adopted) from을 의미하며, 인자 자체는 오른쪽에 -- 있는 형을 받지만 왼쪽에 있는 형으로 변환된다는 의미이다. -- 오른쪽에 있는 형이 생략되거나 "*"이면 Object를 사용한다. end fun blah(a: [*], b: {*}, c...: {* => Integer}) -- "*"는 그 안에 들어 가는 형을 기본값으로 한다는 의미이다. -- 특히 리스트 등등에 쓰일 경우 adopt라기보다는 parametric -- type을 의미하게 된다. (하지만 c에 있는 *는 Symbol이 된다) end fun blah(a: (Integer, Float, ..., ^...) -> *) -- a의 형식은 def foo(:Integer, :Float, ..., ^...): *이나 -- 그와 호환되는, 예를 들어 (:Integer, :Float, :String, ..., ^...): * -- 또한 사용 가능하다. (함수의 subtype에 기반함) end fun blah(filename: String or nil, :read or :write or :append) -- #TODO: conjunction을 아예 지원하지 않는 게 좋을 수도 있음 -- "or"과 "and"는 자료형의 disjunction 및 conjunction을 나타낸다. -- conjunction은 타입엔 쓰일 수 없고 심볼에만 쓰일 수 있다. end - * 타입을 automatic type inference를 위해 쓴다? - :...를 모든 option을 받기 위해서 사용할 수 있나? - blah := fun ...; end 형태를 선호한다. - multiple dispatch semantics 정확히... - fun (a) + (b) <--------> #+# := fun (a, b) syntactic sugar는 빼는 쪽으로 가자. Ruby missing_method에 해당하는 함수 명은 "#.#", 설정은 "#.#=#" - generator semantics 확정. (yield와 함께) ------------ 괄호로 묶어 호출하는 방법: -- 다음은 모두 함수 호출이다. (println "asdf") (println "asdf", "foo") (naru.core.println "bar") (IO[:stderr].write "hmm", :flush) -- 다음은 함수 호출이 아니다. (println) -- 그냥 값 반환 (println, "asdf", "foo") -- 3-tuple (println +3) -- @+@(println, 3) (println ("asdf"), 3) -- (println("asdf"), 3)과 같음 -- 하지만 예외적으로 다음 문장은 함수 호출이다. println -- 하나의 문장이 atom일 경우 -- [TBD] 함수 호출에서 ( 앞의 공백으로 함수 호출과 괄호를 구별해야? -- perl 6의 접근 방법과 유사함. ----------- function attribute namespace Imaging class Reader def read(file: IO) with :interface -- ... will ignore this body end end class JpegReader <- Reader def read(file: IO) with :override, extern: '__N_Imaging_JpegReader_read' -- ... also will ignore this body, except externing is failed end end end ------------ -- function prototype 총정리 fun (arg); end -- arg는 아무 타입이나 받을 수 있으며 그 반환값도 아무 거나 -- 정확히는, fun (arg: Object): Object와 같음 fun (arg: Integer): Integer; end -- Integer를 받고 Integer를 반환해야 한다. fun[T] (arg1: List[T], arg2: T): T; end -- 템플릿 인자 T가 주어진다. fun[T]는 fun[T: Type]과 동일하다. fun[value: Integer] (): Integer; end -- 템플릿 인자 value는 정수여야 한다. fun[value is 3]? ## class - 객체 모델: prototype-like 시스템을 쓸 것 같음. 클래스와 객체의 실질적인 차이는 별로 없으며... 클래스는 자기 인스턴스에 대한 메소드 뿐만 아니라 클래스 메소드도 정의 가능. (self.something?) - 클래스 정의 안에서 기본 네임스페이스는 prototype이고, self는 클래스 자기 자신을 나타낸다. 클래스는 self.@(@)를 호출하면서 prototype으로부터 인스턴스를 만들어 낸다. - 클래스의 self.@.@는 일단 클래스 자기 자신의 네임스페이스로부터 찾아 보고, 없으면 prototype을 뒤진다. 전자의 경우 bounded method가 반환되고, 후자의 경우 raw function이 반환된다. (인스턴스의 경우에도 마찬가지로 작동한다. 단지 prototype이 딱히 없으니 문제지.) 이런 동작은 self.@.@를 재정의해서 바꿀 수 있으며, 인스턴스에서도 마찬가지로 작동한다. - 모든 메소드는 !로 끝나는 이름(이미 있지 않다면)을 자동으로 정의하려 한다. obj.method! *args는 obj = obj.method *args로 정의된다고 볼 수 있다. (물론 반환 타입이 맞아야 함.) @로 시작하는 이름은 예외. 이 동작은 self.@.@의 작동과 관련되어 있다. - @로 시작하는 이름은 (연산자 이름과의 clarification 필요) private으로, 바깥에서 안 보인다고 가정하고 쓸 수 있는 이름이다. 물론 바깥에서 볼 수는 있지만 prefix가 필요하다. (사실은 private method도 비슷하게 정의 가능하다!) 또한 컴파일러는 이 네임스페이스에 있는 이름들을 마음대로 갈아 엎던지 할 수 있기 때문에 reflection의 목적이 아니면 이용하지 않는 게 좋다. - 객체 모델: 정해진 거 없음. 정확한 관계가 모호함. - 클래스 정의 안에서 scope는 인스턴스에 복사되지만 self에 설정되는 것은 복사되지 않는다. Blah := class foo := fun ...; end -- 인스턴스 메소드 self.bar := fun ...; end -- 클래스 메소드 end - operator overloading 관련 정확한 정의 - @로 시작하는 variable은 이제 항상 클래스 내부 변수로 사용된다. (operator는 모두 #로 옮겨 갔음) optimization 관련된 부분은 아직도 유효. reflection 아니면 자제할 것. --------- parametric types: 일종의 metaclass를 만든다고 생각하면 좋을 듯. --------- every generated classes are cached inside TypeName[...]. -- []로 묶은 것은 타입 정보를 의미한다. class Something[T] -- 이 문장은 Something의 새 instance가 만들어질 때마다 실행된다. println T.inspect end class Something[T] -- extension -- 이 문장도 새 instance가 만들어질 때마다 실행된다. -- parametric type은 사실상 다음과 같은 정의를 의미한다: (-- class Something; end class Something_Integer <- Something; end class Something_Float <- Something; end ... --) -- 따라서 새로 추가되는 문장들은 모든 인스턴스 Something_*에 대해서 -- 한 번씩 실행된다. (왜냐하면 모든 인스턴스에 적용되어야 하니까!) println T.name end ------ subtype type evenint = Integer and fun (s) -> s % 2 == 0 (-- same as: class evenint <- Integer invariant := fun assert self % 2 == 0 end end --)