모나드

Ada·2023년 9월 7일

Akka

목록 보기
12/32

모나드란?

프로그래밍에서 값을 감싼(wrap) 상자 같은 것.

퓨처와 에이전트 또한 모나드이다.

몇 가지 특별한 규칙과 연산을 따름.

모나드 주요 연산

  1. unit 또는 pure : 일반 값을 모나드로 감싸는 연산
  1. map : 모나드가 감싼 값을 변환

  2. flatMap 또는 bind : 모나드가 감싼 값을 또 다른 모나드로 변환

이 세 가지 연산을 만족하는 타입은 모나드로 볼 수 있다.

예시 : Option, List, Future

Option

// pure (값을 Option으로 감쌈)
val someValue: Option[Int] = Some(2)
val noValue: Option[Int] = None

// map (값에 함수를 적용하고 Option으로 다시 감쌈)
val added = someValue.map(_ + 1)  // Some(3)
val notAdded = noValue.map(_ + 1)  // None

// flatMap (값에 함수를 적용하고 그 결과가 Option이면 그대로, 아니면 Option으로 감쌈)
val flatMapped = someValue.flatMap(x => Some(x + 1))  // Some(3)
val notFlatMapped = noValue.flatMap(x => Some(x + 1))  // None

option 을 사용하면 값이 있을 수도 있고 없을 수도 있다는 것을 표현할 수 있다.


List

// pure는 일반적으로 List(_) 또는 List.apply를 씀
val single = List(1)  // List[Int] = List(1)

// map
val nums = List(1, 2, 3)
val doubled = nums.map(_ * 2)  // List(2, 4, 6)

// flatMap
val flatMapped = nums.flatMap(x => List(x, x * 10))  // List(1, 10, 2, 20, 3, 30)

List 에서는 map 이 각 원소에 함수를 적용하고, flatMap 은 각 원소에 함수를 적용한 후 모든 리스트를 합친다.


Future


import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

// pure (Future.successful로 감쌈)
val f: Future[Int] = Future.successful(1)

// map
val g = f.map(_ + 1)  // Future에 1을 더함. 결과는 Future[Int]

// flatMap
val h = f.flatMap(x => Future(x + 2))  // Future에 2를 더한 새로운 Future를 반환

Future 는 비동기 연산의 결과를 감싼 상자같은 거라고 볼 수 있다.
map 과 flatMap 을 통해 이 결과를 다룰 수 있다.




모나드를 이해하면 코드가 더 일관적이고 예측 가능해진다.
같은 패턴과 연산을 다양한 타입에 적용할 수 있기 때문이다.

즉, '상자'인 모나드 안에 값이 들어있고, 이 값을 가지고 무언가를 할 수 있으면서
그 결과도 같은 종류의 '상자' 로 유지할 수 있도록 하여 복잡한 작업이나 상태를 더 단순하게 다룰 수 있다는 것이 모나드의 장점이다.



Future & Agent

Future 와 Agent 가 모나드라고 하는 것은 이들이 모나드의 세 가지 기본 규칙을 따르기 때문이다.

값을 감싸기(unit), 값 변환(map), 그리고 새로운 모나드 생성(flatMap)

Future 의 경우 비동기 연산을 '감싼 상자' 라고 볼 수 있다.

map, flatMap 등의 연산을 사용해서 이 '상자' 안의 값을 변환하거나 새로운 Future 를 생성할 수 있다.

Agent 의 경우, 모나드의 개념을 덜 직접적으로 따르지만, 비슷한 논리가 적용된다.

Agent 는 상태를 안전하게 관리하기 위한 방법 중 하나로, 내부 상태에 안전하게 접근하거나 변경할 수 있게 도와준다.

send 또는 alter 같은 메서드를 통해 상태를 변경하면, 이 변경 작업은 Future 로 감싸저셔 비동기적으로 처리되고, 이 Future 를 통해 작업의 성공 여부나 결과를 확인할 수 있다.

Agent 의 예시

아카에서의 Agent 는 상태를 안전하게 변경할 수 있는 컨테이너로 생각할 수 있다.

import akka.agent.Agent
import akka.actor.ActorSystem
import scala.concurrent.ExecutionContext.Implicits.global

implicit val system = ActorSystem("agent-example")
val agent = Agent(5)  // 초기값이 5인 에이전트 생성

val future1 = agent.send(_ + 2)  // 상태를 2 더함, 반환값은 Future[Int]
val future2 = agent.alter(_ * 3)  // 상태를 3배로 만듦, 반환값은 Future[Int]

여기서 send 와 alter 메서드는 비동기적으로 상태를 변경하기 때문에 Future 를 반환한다.

이 Future 를 사용함으로써 상태 변경 작업이 성공했는지, 실패했는지, 어떤 결과를 반환하는지 등을 알 수 있다.

모나드의 개념이 Agent 에 적용되는 부분은 이런 비동기 상태 변경 작업을 추상화하고,
이를 Future형태로 다룰 수 있게 해준다는 점이다.

Future를 이용하면 더 복잡한 로직이나 에러 처리 등을 쉽게 할 수 있다.

profile
백엔드 프로그래머

0개의 댓글