프로

프로토콜 에서 Elixir

4개의 연습 문제

프로토콜 소개

프로토콜은:

  • 다형성을 구현하는 메커니즘이에요.
  • defprotocol로 정의해요.
  • defimpl로 특정 데이터 타입에 대해 구현해요.

프로토콜의 큰 장점은, 특정 데이터 타입에 대한 프로토콜 구현이 그 데이터 타입의 정의나 프로토콜의 정의와 함께 묶여 있을 필요가 없다는 점이에요. 덕분에 라이브러리를 수정하지 않고도, 사용자가 자신만의 데이터 타입에서도 동작하도록 라이브러리를 쉽게 확장할 수 있어요.

구현하기

엘릭서의 모든 타입, 즉 Tuple, Atom, List, BitString, Integer, Float, Function, PID, Map, Port, Reference, Any와 구조체에 대해 프로토콜을 구현할 수 있어요.

프로토콜은 한 번에 여러 타입에 대해 구현할 수 있어요:

defimpl Reversible, for: [Map, List] do
  def reverse(term) do
    Enum.reverse(term)
  end
end

구조체를 정의하는 모듈 안에서 그 구조체에 대한 프로토콜을 직접 구현할 때는 :for 인자를 생략할 수 있어요:

defmodule ShoppingCart do
  defstruct [:items]

  defimpl Reversible do
    def reverse(shopping_cart) do
      Enum.reverse(shopping_cart)
    end
  end
end

Any로의 폴백

기본적으로, 구현되지 않은 데이터 타입에 대해 프로토콜을 호출하면 Protocol.UndefinedError 오류가 발생해요:

to_string({})
# => ** (Protocol.UndefinedError) protocol String.Chars not implemented for {} of type Tuple
#        (elixir 1.10.3) lib/string/chars.ex:3: String.Chars.impl_for!/1
#        (elixir 1.10.3) lib/string/chars.ex:22: String.Chars.to_string/1

이런 경우에 사용될 폴백 구현을 제공하면 이 문제를 피할 수 있어요. 그러려면 프로토콜을 정의할 때 @fallback_to_any 모듈 속성을 true로 설정하고, Any 타입에 대한 구현을 작성하면 돼요:

defprotocol Reversible do
  @fallback_to_any true
  def reverse(term)
end

defimpl Reversible, for: Any do
  def reverse(term) do
    term
  end
end

참고로, 대부분의 경우에는 오류를 발생시키는 쪽이 더 바람직해요. 모든 데이터 타입에 대해 프로토콜에 어울리는 합리적인 가정을 세우기는 어렵기 때문이에요.

주목할 만한 내장 프로토콜

  • Enumerable - Enum과 Stream 모듈의 함수들은 이 프로토콜을 구현한 데이터 타입과 함께 동작해요.
  • Collectable - Enum.into/2 함수는 이 프로토콜을 사용해서 열거 가능한 값을 컬렉션에 넣어요.
  • Inspect - 이 프로토콜을 구현하면 Kernel.inspect/2가 특정 데이터 타입을 문자열로 표현하는 방식을 바꿀 수 있어요.
  • String.Chars - 이 프로토콜을 구현하면 Kernel.to_string/1이 특정 데이터 타입을 문자열로 표현하는 방식을 바꿀 수 있어요.
GitHub에서 편집 링크가 새 창이나 탭에서 열려요

프로토콜 배우기

연습이 잠겨 있어요

프로토콜 개념을 연습하려면 연습 문제 1개를 더 잠금 해제해요