프로그래밍에서 값을 감싼(wrap) 상자 같은 것.
퓨처와 에이전트 또한 모나드이다.
몇 가지 특별한 규칙과 연산을 따름.
unit 또는 pure : 일반 값을 모나드로 감싸는 연산map : 모나드가 감싼 값을 변환
flatMap 또는 bind : 모나드가 감싼 값을 또 다른 모나드로 변환
이 세 가지 연산을 만족하는 타입은 모나드로 볼 수 있다.
// 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 을 사용하면 값이 있을 수도 있고 없을 수도 있다는 것을 표현할 수 있다.
// 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 은 각 원소에 함수를 적용한 후 모든 리스트를 합친다.
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 가 모나드라고 하는 것은 이들이 모나드의 세 가지 기본 규칙을 따르기 때문이다.
값을 감싸기(unit), 값 변환(map), 그리고 새로운 모나드 생성(flatMap)
Future 의 경우 비동기 연산을 '감싼 상자' 라고 볼 수 있다.
map, flatMap 등의 연산을 사용해서 이 '상자' 안의 값을 변환하거나 새로운 Future 를 생성할 수 있다.
Agent 의 경우, 모나드의 개념을 덜 직접적으로 따르지만, 비슷한 논리가 적용된다.
Agent 는 상태를 안전하게 관리하기 위한 방법 중 하나로, 내부 상태에 안전하게 접근하거나 변경할 수 있게 도와준다.
send 또는 alter 같은 메서드를 통해 상태를 변경하면, 이 변경 작업은 Future 로 감싸저셔 비동기적으로 처리되고, 이 Future 를 통해 작업의 성공 여부나 결과를 확인할 수 있다.
아카에서의 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를 이용하면 더 복잡한 로직이나 에러 처리 등을 쉽게 할 수 있다.