Ruby OOP에서 Elixir Functional로 예시를 통해
발행: (2025년 12월 2일 오후 01:00 GMT+9)
4 min read
원문: Dev.to
Source: Dev.to
Ruby 구현
함수형 프로그래밍을 배우는 가장 효과적인 방법 중 하나는 OOP 언어로 작성한 동일한 기능을 함수형 스타일로 변환하는 것입니다. 아래는 Ruby로 구현한 간단한 ToyRobot 객체이며, 세 가지 속성(x, y, direction)과 동작(move, turn left, turn right)을 가지고 있습니다.
class ToyRobot
attr_reader :x, :y, :direction
DIRECTIONS = [:north, :east, :south, :west]
BOUNDS = {x: 0..10, y: 0..10}
def initialize
@x = 0
@y = 0
@direction = :north
end
def place(x, y, direction)
if DIRECTIONS.include?(direction)
@x = x
@y = y
@direction = direction
else
raise "Invalid direction"
end
end
def report
{x: @x, y: @y, direction: @direction}
end
def move
case @direction
when :north then @y += 1
when :east then @x += 1
when :south then @y -= 1
when :west then @x -= 1
end
if @x > BOUNDS[:x].max || @y > BOUNDS[:y].max
raise "Out of bounds"
end
end
def right
case @direction
when :north then @direction = :east
when :east then @direction = :south
when :south then @direction = :west
when :west then @direction = :north
end
end
def left
# implement left
end
end
Elixir 구현
아래는 Elixir로 표현한 동일한 로봇입니다. Elixir가 처음이라면 코드를 보기 전에 직접 구현해 보세요.
defmodule ToyRobot do
@directions [:north, :east, :south, :west]
@bounds_x 0..20
@bounds_y 0..20
defstruct x: 0, y: 0, direction: :east
def new() do
{:ok, robot} = place(0, 0, :east)
robot
end
def place(_x, _y, direction) when direction not in @directions do
{:error, :invalid_direction}
end
def place(x, y, _direction) when x not in @bounds_x or y not in @bounds_y do
{:error, :out_of_bounds}
end
def place(x, y, direction) do
{:ok, %ToyRobot{x: x, y: y, direction: direction}}
end
def report(%ToyRobot{x: x, y: y, direction: direction}) do
{x, y, direction}
end
def right(%ToyRobot{direction: direction} = robot) do
new_direction =
case direction do
:north -> :east
:east -> :south
:south -> :west
:west -> :north
end
%ToyRobot{robot | direction: new_direction}
end
def left(%ToyRobot{direction: direction} = robot) do
# implement right
end
def move(%ToyRobot{x: x, y: y, direction: direction} = robot) do
{new_x, new_y} =
case direction do
:north -> {x, y - 1}
:east -> {x + 1, y}
:south -> {x, y + 1}
:west -> {x - 1, y}
end
if new_x in @bounds_x and new_y in @bounds_y do
%ToyRobot{robot | x: new_x, y: new_y}
else
robot
end
end
end
핵심 차이점
메서드 vs 함수
- Ruby (OOP): 메서드는 클래스에 속하며 객체의 내부 상태를 조작합니다.
robot = ToyRobot.new
robot.move
robot.left
- Elixir (Functional): 모듈은 함수를 그룹화하며, 데이터를 명시적으로 전달합니다.
robot = ToyRobot.new()
ToyRobot.move(robot)
ToyRobot.left(robot)
Elixir에서는 필요한 모든 데이터를 인자로 전달해야 하지만, Ruby 메서드는 객체의 상태에 암묵적으로 접근할 수 있습니다.
가변 vs 불변
- Ruby: 메서드는 객체의 상태를 변경합니다.
robot = ToyRobot.new
robot.move # @x, @y change
robot.report # => {x: 0, y: 1, direction: :north}
- Elixir: 데이터 구조는 불변이며, 함수는 새로운 구조체를 반환합니다.
robot = ToyRobot.new()
# => %ToyRobot{x: 0, y: 0, direction: :east}
new_robot = ToyRobot.move(robot)
# => %ToyRobot{x: 1, y: 0, direction: :east}
robot
# => %ToyRobot{x: 0, y: 0, direction: :east} # unchanged
Elixir의 파이프 연산자(|>)는 변환을 연쇄적으로 연결하는 것을 간결하게 만들어 줍니다:
robot = ToyRobot.new()
|> ToyRobot.move()
|> ToyRobot.move()
|> ToyRobot.right()
|> ToyRobot.left()
결론
근본적인 차이는 상태를 다루는 방식에 있습니다:
- Ruby (OOP) – “객체가 자신의 상태를 변경한다.”
- Elixir (Functional) – “함수가 데이터를 받아 새로운 데이터를 반환한다.”