루비 2.7.0-preview3 릴리스

루비 2.7.0-preview3 릴리스를 알리게 되어 기쁩니다.

프리뷰 버전은 12월에 예정된 최종 릴리스에 대한 의견을 모으기 위해서 릴리스됩니다. preview3은 키워드 인자의 호환성 문제를 확인하기 위해서 릴리스됩니다.

이는 많은 새 기능과 성능 향상을 포함하고 있습니다. 특히 눈에 띄는 것은 다음과 같습니다.

  • 압축 GC
  • 패턴 매칭
  • REPL 개선
  • 위치 인자와 키워드 인자 분리

압축 GC

이 릴리스는 단편화된 메모리를 최적화할 수 있는 압축 GC를 도입합니다.

몇몇 멀티 스레드를 이용하는 루비 프로그램은 메모리 단편화를 일으킬 수 있고, 이는 과다한 메모리 사용과 성능 저하로 이어질 수 있습니다.

힙 공간을 압축하는 GC.compact 메서드가 도입되었습니다. 이 함수는 더 적은 페이지를 사용하고, 힙이 CoW(Copy on Write)에 유리하도록 힙 내부에 살아있는 객체들을 압축합니다. [Feature #15626]

패턴 매칭 [실험적]

함수형 언어에서 널리 사용되는 패턴 매칭 기능이 실험적으로 도입되었습니다. [Feature #14912]

이는 주어진 객체를 순회하다가 패턴이 일치하는 경우 그 값을 대입합니다.

require "json"

json = <<END
{
  "name": "Alice",
  "age": 30,
  "children": [{ "name": "Bob", "age": 2 }]
}
END

case JSON.parse(json, symbolize_names: true)
in {name: "Alice", children: [{name: "Bob", age: age}]}
  p age #=> 2
end

더 자세한 설명은 Pattern matching - New feature in Ruby 2.7을 확인해 주세요.

REPL 개선

루비에 포함되어 있는 상호작용 환경(REPL; Read-Eval-Print-Loop)인 irb가 이제 여러 줄 편집을 지원합니다. 이는 readline과 호환되는 순수 루비 구현인 reline으로 동작합니다. 또한 rdoc 통합도 제공됩니다. irb에서 주어진 클래스, 모듈, 메서드의 레퍼런스를 볼 수 있습니다. [Feature #14683], [Feature #14787], [Feature #14918]

그뿐만 아니라, binding.irb에서 보이는 소스 코드나 코어 클래스 객체의 inspect 결과에 색이 추가되었습니다.

위치 인자와 키워드 인자 분리

키워드 인자와 위치 인자의 자동 변환이 폐기 예정 상태가 되었습니다. 이 변환은 루비 3에서 제거될 예정입니다. [Feature #14183]

  • 메서드 호출이 마지막 인자로 Hash를 넘기며 아무 키워드도 넘기지 않을 때, 호출된 메서드는 키워드를 받을 때 경고가 발생합니다. 계속 키워드로 취급되도록 하려면, 이중 스플랫(double splat) 연산자를 추가해서 경고를 피하고 루비 3에서 올바르게 동작하도록 하세요.

    def foo(key: 42); end; foo({key: 42})   # warned
    def foo(**kw);    end; foo({key: 42})   # warned
    def foo(key: 42); end; foo(**{key: 42}) # OK
    def foo(**kw);    end; foo(**{key: 42}) # OK
    
  • 메서드 호출이 키워드를 넘기고 호출된 메서드도 키워드를 받을 때, 필요한 위치 인자가 부족한 경우 키워드들을 마지막 위치 인자로 간주하고 경고가 발생합니다. 경고를 피하고 루비 3에서 올바르게 동작하도록 하려면 인자를 키워드 대신 해시로 넘기세요.

    def foo(h, **kw); end; foo(key: 42)      # warned
    def foo(h, key: 42); end; foo(key: 42)   # warned
    def foo(h, **kw); end; foo({key: 42})    # OK
    def foo(h, key: 42); end; foo({key: 42}) # OK
    
  • 메서드가 키워드 스플랫(splat)은 받지 않고 특정 키워드는 받을 때, 심볼과 심볼이 아닌 키를 모두 포함한 해시 또는 키워드 스플랫을 메서드에 넘긴 경우 경고가 발생합니다. 루비 3에서 올바르게 동작하려면 메서드를 호출하는 코드가 별도의 해시를 넘기도록 수정해야 합니다.

    def foo(h={}, key: 42); end; foo("key" => 43, key: 42)   # warned
    def foo(h={}, key: 42); end; foo({"key" => 43, key: 42}) # warned
    def foo(h={}, key: 42); end; foo({"key" => 43}, key: 42) # OK
    
  • 메서드가 키워드를 받지 않는데 키워드와 함께 호출되면, 키워드는 위치 인자에 해당하는 해시로 인식되고, 경고가 발생하지 않습니다. 이 동작은 루비 3에서도 유지될 것입니다.

    def foo(opt={});  end; foo( key: 42 )   # OK
    
  • 메서드가 임의의 키워드를 받으면 심볼이 아닌 키도 키워드 인자로 허용됩니다. [Feature #14183]

    def foo(**kw); p kw; end; foo("str" => 1) #=> {"str"=>1}
    
  • 메서드가 키워드를 받지 않음을 명시하기 위해 메서드 정의에서 **nil을 사용할 수 있습니다. 이러한 메서드를 키워드와 함께 호출하면 ArgumentError가 발생합니다. [Feature #14183]

    def foo(h, **nil); end; foo(key: 1)       # ArgumentError
    def foo(h, **nil); end; foo(**{key: 1})   # ArgumentError
    def foo(h, **nil); end; foo("str" => 1)   # ArgumentError
    def foo(h, **nil); end; foo({key: 1})     # OK
    def foo(h, **nil); end; foo({"str" => 1}) # OK
    
  • 키워드를 받지 않는 메서드에 빈 키워드 스플랫을 넘겼을 때, 더 이상 빈 해시를 넘기지 않습니다. 파라미터가 필요한 경우 빈 해시를 넘기지만, 경고가 발생합니다. 위치 인자로서 해시를 넘기려면 이중 스플랫(double splat)을 제거하세요. [Feature #14183]

    h = {}; def foo(*a) a end; foo(**h) # []
    h = {}; def foo(a) a end; foo(**h)  # {} and warning
    h = {}; def foo(*a) a end; foo(h)   # [{}]
    h = {}; def foo(a) a end; foo(h)    # {}
    

NOTE: 키워드 인자 비호환으로 인한 너무 많은 폐기 경고가 번잡하다는 의견이 있습니다. 현재 두 가지 해결책을 의논중입니다. 하나는 기본 설정을 폐기 경고를 비활성화하는 것(#16345)과 중복되는 경고의 출력을 생략하는 것(#16289)입니다. 아직 결정이 나지 않았습니다만, 공식 릴리스 이전에 정해질 것입니다.

이외의 주목할 만한 새 기능

  • 메서드 참조 연산자 .:가 이전 프리뷰에서 실험적으로 도입되었지만 취소되었습니다. [Feature #12125], [Feature #13581], [Feature #16275]

  • 번호 지정 파라미터가 기본 블록 파라미터로서 실험적으로 도입되었습니다. [Feature #4475]

  • 시작 값을 지정하지 않는 범위 연산자가 실험적으로 추가됩니다. 종료 지정이 없는 범위 연산자처럼 유용하지 않을 수도 있습니다만, DSL 용도로는 유용할 것입니다. [Feature #14799]

    ary[..3]  # identical to ary[0..3]
    rel.where(sales: ..100)
    
  • Enumerable#tally가 추가됩니다. 이는 각 요소가 몇 번 출현했는지를 셉니다.

    ["a", "b", "c", "b"].tally
    #=> {"a"=>1, "b"=>2, "c"=>1}
    
  • self에 private 메서드를 호출하는 것이 허용됩니다. [Feature #11297], [Feature #16123]

    def foo
    end
    private :foo
    self.foo
    
  • Enumerator::Lazy#eager가 추가됩니다. 지연 열거자(lazy enumerator)에서 지연 없는 열거자를 생성합니다. [Feature #15901]

    a = %w(foo bar baz)
    e = a.lazy.map {|x| x.upcase }.map {|x| x + "!" }.eager
    p e.class               #=> Enumerator
    p e.map {|x| x + "?" }  #=> ["FOO!?", "BAR!?", "BAZ!?"]
    

성능 향상

  • JIT [실험적]

    • 최적화 가정이 유효하지 않은 경우 JIT으로 컴파일된 코드는 최적화 레벨이 낮은 코드로 재컴파일됩니다.

    • 순수하다고 판단된 메서드를 인라인으로 삽입하게 됩니다. 이 최적화는 아직 실험적이며 많은 메서드는 아직 순수하다고 판단되지 않는 상태입니다.

    • --jit-min-calls의 기본값이 5에서 10,000으로 변경됩니다.

    • --jit-max-cache의 기본값이 1,000에서 100으로 변경됩니다.

  • Symbol#to_s (취소되었습니다.), Module#name, true.to_s, false.to_s, nil.to_s가 이제 항상 얼린 문자열을 반환합니다. 주어진 객체에 대해 항상 동일한 문자열이 반환됩니다. [실험적] [Feature #16150]

  • CGI.escapeHTML의 성능이 향상되었습니다. GH-2226

  • Monitor와 MonitorMixin의 성능이 향상되었습니다. [Feature #16255]

그 이외의 2.6 이후로 주목할 만한 변경

  • 표준 라이브러리를 업데이트했습니다.
    • Bundler 2.1.0.pre.3 (History)
    • RubyGems 3.1.0.pre.3 (History)
    • CSV 3.1.2 (NEWS)
    • Racc 1.4.15
    • REXML 3.2.3 (NEWS)
    • RSS 0.2.8 (NEWS)
    • StringScanner 1.0.3
    • 기존 버전이 없는 다른 몇몇 라이브러리도 업데이트되었습니다.
  • 표준 라이브러리가 기본 젬으로 승격됩니다.
    • 다음 기본 젬들이 rubygems.org 에서 배포중입니다.
      • benchmark
      • cgi
      • delegate
      • getoptlong
      • net-pop
      • net-smtp
      • open3
      • pstore
      • singleton
    • 다음 기본 젬들은 ruby-core에서는 젬으로 승격되었지만, 아직 rubygems.org에서 배포하고 있지 않습니다.
      • monitor
      • observer
      • timeout
      • tracer
      • uri
      • yaml
  • 블록을 넘긴 메서드의 호출 안에서 블록이 없는 Proc.newproc을 사용하면 경고가 발생합니다.

  • 블록을 넘긴 메서드의 호출 안에서 블록이 없는 lambda는 에러가 발생합니다.

  • 유니코드와 에모지의 버전을 11.0.0에서 12.0.0으로 업데이트했습니다. [Feature #15321]

  • 유니코드를 일본의 새로운 연호 레이와를 가리키는 코드(U+32FF SQUARE ERA NAME REIWA)에 대한 지원을 추가한 12.1.0으로 업데이트했습니다. [Feature #15195]

  • Date.jisx0301, Date#jisx0301, Date.parse에서 새 일본 연호를 지원합니다. [Feature #15742]

  • 루비 빌드에 C99를 지원하는 컴파일러를 요구합니다. [Misc #15347]
  • Regexp#match{?}nil을 넘기면 String, Symbol에서처럼 TypeError를 던집니다. [Feature #13083] 취소되었습니다.

NEWS커밋 로그에서 더 자세한 설명을 확인할 수 있습니다.

이러한 변경사항에 따라, 루비 2.6.0 이후로 파일 3895개 수정, 213426줄 추가(+), 96934줄 삭제(-)가 이루어졌습니다!

루비 2.7을 즐겨주시기 바랍니다!

다운로드

  • https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.0-preview3.tar.bz2

    SIZE: 14630824
    SHA1: 1fa35d8a26dfc814e92fa259095f4cf70f386f87
    SHA256: df2ddee659873e6fc30a8590ecffa49cf3a4ef81fa922b0d09f821b69ee88bc3
    SHA512: 5d8e99e3fd984c7d05c0bc483e1504e81ccdb920cbb2d78cad3c314e197b30316b692fd0199f836acac41246e3a713cb81dc6dd64c27cba56f807df4c193db1a
    
  • https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.0-preview3.tar.gz

    SIZE: 16723536
    SHA1: 7554926ee29a344da4b53d67fc296d70fdbe60ca
    SHA256: 9baa1f5096ebc2a0923df628d7dc7105da3789c1bf8b873469d9010249736b00
    SHA512: 8fad3e761fd54036fee974a9f33e4db31d9a8a878b1181a08724388f5a1da548ab249136356f675797e9c43b565777bf22e6a419db1364336f134b31f4e75b33
    
  • https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.0-preview3.tar.xz

    SIZE: 11923988
    SHA1: f3c54538915483e5ddc714ac23414e7c47048b12
    SHA256: ad9d61e55ac224e3447a762e001965839846f9658f87a0e792840887cfe61b8c
    SHA512: 2b6844f34d32f1013dc3110043e6ece33a083b20f1343dea9a14311bda0017e8f56fc7d73be1616999b22ce430d7ba59a77bb0892d27c6d1ec243c3860086133
    
  • https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.0-preview3.zip

    SIZE: 20691541
    SHA1: d18b494cda4db751d8b3f5026404e348f3f682e3
    SHA256: 2bc95f67f271b6a41fc3dd40536705b4a7974df8a2fa33ff0758a60822291781
    SHA512: af9f728aebc53693cbd9f78a632c82e851e9f83dfc0c53979fdc37c627b11482c8435ce12dbb1d5a7253e998ea989759be699e6a00aae18384d2d765650cb0d7
    

루비는

루비는 1993년에 Matz(마츠모토 유키히로)가 처음 개발했고, 현재는 오픈 소스로서 개발되고 있습니다. 이는 여러 플랫폼에서 동작하며, 특히 웹 개발에서 전 세계적으로 이용되고 있습니다.