
엘릭서(Elixir)는 얼랭(Erlang) 가상 머신(BEAM) 위에서 동작하는 함수형, 동시성 프로그래밍 언어이다. 엘릭서는 얼랭이 보유하고 있는 분산 처리, 장애 내구성, 실시간, 무정지 애플리케이션 등의 특징을 공유한다. 그에 더해서 프로토콜을 이용해 다형성을 지원하고, Quote과 Unquote그리고 Macro를 통한 DSL 구현 등의 메타 프로그래밍이 가능하다.
-- wikipedia
몇가지 생소한 개념들이 많아 이해하는 과정이 어렵지만 그만큼 재미도 있다.
요즘은 세상이 좋아져서, 언어를 배우는 학습도구가 많이 발달한 것 같다. 예전에는 두꺼운 책을 끼고 끙끙대면서 한글자한글자 쳐보면서 고생했던 기억이 떠오른다.
엘릭서는 아무래도 마이너 언어답게, 참고할만한 자료가 그리 많지 않다. 엘릭서 관련 도서는 작년에 하나 나왔다. 다행히도 얇은편이다. 하지만 언어공부를 책으로 하는 것 만큼 비효율적인 일을 없다고 생각한다.
요즘 거의 모든 개발자가 chatGPT와 함께 개발을 한다. 하지만 알다시피 챗GPT도 인터넷 자료를 기반으로 학습하였기 때문에, 마이너한 언어의 경우 학습량의 부족인지 없는 함수명을 지어내는 경우가 참 많다. (다른 언어는 모르겠지만, Elixir 공부할 때는 비추한다.)
다음에 시간나면 공부하는 법은 정리하도록 하고,
결국 exercism 이란 사이트에서 공부를 시작했다. 사실 이 사이트는 요 몇년간 돌아본 사이트 중 거의 원탑급의 학습 커뮤니티 사이트이다. 여길 모르고 힘들게 공부한게 억울할 정도로 정말 잘 되어있다.
다음과 같이 예쁘게 내가 학습하는 과정을 표현해준다.

언어를 마스터 할 때마다, 뱃지가 추가되며, 커뮤니티 기능이 훌륭해서 내가 문제를 내면서 기여할 수 도있고, 다른 사람이 푼 문제를 리뷰해줄 수 도있다. 이런 커뮤니티 기여도를 아래 그림처럼 또 표현해준다.

비영리단체에서 운영하는 관계로 모두 무료로 사용할 수 있고, 원하는 만큼의 도네이션을 통해 프리미엄 기능을 언락할 수 있다.
개방-폐쇄 원칙 - 확장에는 열려있고 수정에는 닫혀있는 코드가 좋다
뭔가 새로운 기능을 추가할 때(기능을 확장할 때), 기존 코드를 수정하지말고, 덧붙여 가면서 확장하라는 건데, 코드를 보면서 이해해보자.
다음은 숫자맞추기 게임을 만든건데, 여러 조건에 따라 출력되어야 할 문자열이 있다.
이러한 경우 별 생각없이 코딩하면 아래와 같은 코드가 나온다.
defmodule GuessingGame do
def compare(secret_number), do: "Make a guess"
def compare(secret_number, guess) do
cond do
guess == :no_guess -> "Make a guess"
secret_number == guess -> "Correct"
secret_number + 1 == guess -> "So close"
secret_number - 1 == guess -> "So close"
secret_number > guess -> "Too low"
secret_number < guess -> "Too high"
end
end
end
cond 라는 키워드? (사실 elixir에서 cond는 예약어가 아니다..함수다! 정확히는 매크로)의 의미는 conditional 느낌으로 이해하면 된다.
이러이러할때, 이렇게 리턴하세요 if else 랑 사용법이 같으니 다른언어의 if-else 라고 보면된다.
guess라는 argument에 값을 전달하지 않으면, 맨위의 1의 인자를 받는 함수가 실행되고,
두개의 인자를 넘겨주면, 두번째 compare(secret_number, guess) 가 실행된다.
테스트는 다 통과하고, 사실 이렇게 짜도 돌아는 간다. 다만,
만약에 새로운 조건이 추가되면 어떻게 될까?
익숙하게 cond do 밑에 새로운 줄을 추가하면서 하겠지.
여기서 문제가 발생하는데, 이미 잘 돌아가는 해당 함수 compare/2 (/2는 2개의 인자를 받는 다는 의미)가 내부적으로 수정(변경)된다. 따라서 그동안 저 함수를 호출하는 함수가 다른 값을 리턴받게될 확률이 생긴다. 이러면 이제 새로운 기능 하나 추가됨에 따라, 전체기능이 잘 돌아가는지 다 테스트해봐야하고, 그럼에도 불구하고 찜찜함이 남게 마련이다.
꼭 이런 애들이, 내가 휴가가있거나, 집안에 일이 생겨 빨리 나가봐야할 때 리포트된다.
이런 일은 사실 항상 발생한다. 모든 악의 근원 및 버그가 여기서 나온다해도 과언이 아니라고 생각한다.
고치는건 수정으로 하면안되고, 새롭게 함수를 추가해야한다. 그래서 엘릭서에서는 아래와 같이 함수를 만들라고 가이드한다.
defmodule GuessingGame do
def compare(secret_number, secret_number), do: "Correct"
def compare(_, guess \\ :no_guess) when not is_integer(guess), do: "Make a guess"
def compare(secret_number, guess) when abs(secret_number - guess) == 1, do: "So close" #(1)
def compare(secret_number, guess) when secret_number < guess, do: "Too high" #(2)
def compare(secret_number, guess) when guess < secret_number, do: "Too low"
end
end
심지어 라인수도 줄어든다. when 이하 붙는 가드(Guard)라는 표현식으로 주어진 argument에 대한 깔끔하게 확장이 가능하다.
심지어 같은 값이 들어왔다를 def compare(secret_number, secret_number) 인수 패턴매칭을 통해 바로 리턴;;
물론 위에서부터 밑으로 식을 검증하기 때문에, 빡빡한 조건을(포함관계) 루즈한 표현식보다 상위에 적어야한다. 예를 들어, secret_number와 guess 두 숫자 사이 값이 1이거나 -1이면 "So close"를 반환하는데, secret_number - guess (1) 의 위치가 secret_number > guess (2) 보다 밑에 있으면 해당 분기를 타지 않고 (2)의 출력값이 나올 것이다.
이상으로 Guard를 통해서 같은 함수에 조건에 따른 여러 분기를 if, cond, case 없이 간단히 추가 할 수 있는 Elixir의 특성을 알아보았다.