Traits

Traits

트레이트(trait)는 믹스인(mixin)으로 변환한 다음 클래스에 적용할 수 있는 메서드들의 모음이에요. 트레이트를 믹스인으로 변환하기 전에, 트레이트의 메서드들을 개별적으로 이름을 바꿀 수 있고, 여러 트레이트를 병합해 새 트레이트를 만들 수도 있어요.

출처: Racket Reference

본문

이 절에 문서화된 바인딩들은 racket/trait 라이브러리가 제공하며, racket/baseracket이 제공하는 게 아니에요.

(require racket/trait) ; package: base
trait-clause          = (public maybe-renamed ...)
                      | (pubment maybe-renamed ...)
                      | (public-final maybe-renamed ...)
                      | (override maybe-renamed ...)
                      | (overment maybe-renamed ...)
                      | (override-final maybe-renamed ...)
                      | (augment maybe-renamed ...)
                      | (augride maybe-renamed ...)
                      | (augment-final maybe-renamed ...)
                      | (inherit maybe-renamed ...)
                      | (inherit/super maybe-renamed ...)
                      | (inherit/inner maybe-renamed ...)
                      | method-definition
                      | (field field-declaration ...)
                      | (inherit-field maybe-renamed ...)
(trait trait-clause ...)

syntax

트레이트를 만들어요. trait 폼의 본문은 class* 폼의 본문과 비슷하지만, 비공개(가 아닌) 메서드 정의로 제한돼요. 특히 maybe-renamed, method-definition, field-declaration의 문법은 class*와 같고, 모든 method-definition은 대응하는 선언(public, override 등 중 하나)을 가져야 해요. class에서처럼, 직접 호출, super 호출, inner 호출에서 메서드 이름의 사용은 inherit, inherit/super, inherit/inner 및 같은 트레이트의 다른 메서드 선언을 통해 메서드 이름을 스코프로 가져오는 것에 달려 있어요. class와 비교한 예외는, overment가 메서드 이름을 해당 메서드에서만 바인딩하고 같은 트레이트의 다른 메서드에서는 바인딩하지 않는다는 점이에요. 마지막으로 public*define/public 같은 매크로는 class에서처럼 trait에서 동작해요.

trait, trait-exclude, trait-exclude-field, trait-alias, trait-rename, trait-rename-field 폼의 외부 식별자는 define-member-namedefine-local-member-name을 통한 바인딩 대상이 돼요. trait 폼에서는 비공개 메서드나 필드가 허용되지 않지만, public 또는 field 선언과 그 이름의 스코프를 trait 폼으로 제한함으로써 흉내 낼 수 있어요.

(trait? v) → boolean?
v : any/c

v가 트레이트이면 #t, 그렇지 않으면 #f를 돌려줘요.

(trait->mixin
  (trait trait-clause ...))
(trait->mixin tr) → (class? . -> . class?)
tr : trait?

트레이트를 믹스인으로 변환하며, 이 믹스인은 클래스에 적용해 새 클래스를 만들어낼 수 있어요. 다음 형태의 표현식은

(lambda (%)
  (class % trait-clause ... (super-new)))

다음과 동등해요.

일반적으로 트레이트의 메서드는 믹스인으로 변환하기 전에 변경되고 다른 트레이트와 결합돼요.

(define t1
  (trait
    (define/public (m1) 1)))
(define t2
  (trait
    (define/public (m2) 2)))
(define t3 (trait-sum t1 t2))
(trait-sum tr ...+) → trait?
tr : trait?

주어진 tr들의 모든 메서드를 결합한 트레이트를 만들어요. 예를 들어, 위 코드는 아래와 동등한 트레이트 t3를 만들어요.

(trait
  (define/public (m1) 1)
  (define/public (m2) 2))

하지만 t1t2는 여전히 개별적으로 사용하거나 다른 트레이트와 결합할 수 있어요.

trait-sum으로 트레이트를 결합할 때, 다른 트레이트가 같은 메서드나 필드 이름에 대한 정의를 제공하면 그 결합은 inherit, inherit/super, inherit/inner, inherit-field 선언을 버려요. 결합할 트레이트 중 어느 하나가 같은 이름의 메서드나 필드를 정의하거나, 버려질 inherit/super 또는 inherit/inner 선언이 제공된 정의와 일관되지 않으면 trait-sum 연산은 실패해요(exn:fail:contract 예외가 일어나요). 즉, inherit, inherit/super, inherit/inner로 메서드를 선언하는 것은 그 메서드를 정의하는 것으로 치지 않아요. 동시에, 예를 들어 메서드 m에 대한 inherit/super 선언을 포함하는 트레이트는 maugment로 정의하는 트레이트와 결합될 수 없어요. 트레이트가 나중에 믹스인으로 변환되어 클래스에 적용될 때, augmentinherit/super 둘 다의 요구를 만족시키는 클래스가 없을 수 있기 때문이에요.

(trait-exclude trait-expr id)

syntax

trait-expr의 트레이트 결과와 같지만, id가 이름인 메서드의 정의가 제거된 새 트레이트를 만들어요. 메서드 정의가 제거됨에 따라 inherit, inherit/super, inherit/inner 선언 중 하나가 추가돼요:

  • public, pubment, public-final로 선언된 메서드는 inherit 선언으로 대체돼요.
  • override 또는 override-final로 선언된 메서드는 inherit/super 선언으로 대체돼요.
  • augment, augride, augment-final로 선언된 메서드는 inherit/inner 선언으로 대체돼요.
  • overment로 선언된 메서드는 어떤 inherit 선언으로도 대체되지 않아요.

trait-expr가 만들어낸 트레이트에 id에 대한 메서드 정의가 없으면 exn:fail:contract 예외가 일어나요.

(trait-exclude-field trait-expr id)

syntax

trait-expr의 트레이트 결과와 같지만, id가 이름인 필드의 정의가 제거된 새 트레이트를 만들어요. 필드 정의가 제거됨에 따라 inherit-field 선언이 추가돼요.

(trait-alias trait-expr id new-id)

syntax

trait-expr의 트레이트 결과와 같지만, id가 이름인 메서드의 정의와 선언이 new-id라는 이름으로 복제된 새 트레이트를 만들어요. 결과 트레이트에 대한 일관성 요구는 trait-sum과 같으며, 그렇지 않으면 exn:fail:contract 예외가 일어나요. 이 연산은 id의 다른 사용(예: new-id에 대한 복제된 정의에서의 메서드 호출)은 이름을 바꾸지 않아요.

(trait-rename trait-expr id new-id)

syntax

trait-expr의 트레이트 결과와 같지만, id가 이름인 메서드에 대한 모든 정의와 참조가 new-id가 이름인 메서드에 대한 정의와 참조로 교체된 새 트레이트를 만들어요. 결과 트레이트에 대한 일관성 요구는 trait-sum과 같으며, 그렇지 않으면 exn:fail:contract 예외가 일어나요.

(trait-rename-field trait-expr id new-id)

syntax

trait-expr의 트레이트 결과와 같지만, id가 이름인 필드에 대한 모든 정의와 참조가 new-id가 이름인 필드에 대한 정의와 참조로 교체된 새 트레이트를 만들어요. 결과 트레이트에 대한 일관성 요구는 trait-sum과 같으며, 그렇지 않으면 exn:fail:contract 예외가 일어나요.

더 알아보기