펑터

테크·2023년 2월 12일
0

펑터란

펑터(Functor)는 매핑할 수 있는 것(can be mapped over)이라는 행위를 선언한 타입 클래스를 말한다.

자세히 정리하면 펑터는 리스트 같은 컨테이너형 타입의 값을 꺼내서 입력받은 함수를 적용한 후, 함수의 결괏값을 컨테이너형 타입에 넣어서 반환하는 행위를 선언한 타입 클래스를 말한다.

펑터 자체는 추상화된 타입 클래스이기 때문에 컨테이너형 타입이 가진 구체적인 타입까지 포함해서 정의하지 않는다. (List<Int> 대신 List<T>)

오로지 한 개의 매개변수를 받는 타입생성자(type constructor)다.

Functor 타입 클래스

Kotlin

interface Functor<out A> {
	fun <B> fmap(f: (A) -> B): Functor<B>
}

Haskell

class Functor f where
	fmap : (a -> b) -> f a -> f b

scala

trait Functor[F[_]] {
	def map[A, B](fa: F[A])(f: A => B): F[B]
}

Maybe 펑터 구현

sealed class Maybe<out A>: Functor<A> {
  abstract override fun toString(): String
  abstract override fun <B> fmap(f: (A) -> B): Maybe<B>
}

data class Just<out A>(val value: A): Maybe<A>() {

  override fun toString(): String = "Just($value)"

  override fun <B> fmap(f: (A) -> B): Maybe<B> = Just(f(value))
}

object Nothing: Maybe<kotlin.Nothing>() {

  override fun toString(): String = "Nothing"

  override fun <B> fmap(f: (kotlin.Nothing) -> B): Maybe<B> = Nothing
}

fun main() {
  println(Just(10).fmap { it + 10 }) // Just(20)
  println(Nothing.fmap { a: Int -> a + 10 }) // Nothing
}

펑터는 타입 생성자에서 컨테이너형 타입을 요구한다. 따라서 어떤 값을 담을 수 있는 타입은 항상 펑터로 만드는 것을 생각해 볼 수 있다.

펑터의 법칙

  1. 항등 함수(identity function)에 펑터를 통해서 매핑하면 반환되는 펑터는 원래의 펑터와 같다
  2. 두 함수를 합성한 함수의 매핑은 각 함수를 매핑한 결과를 합성한 것과 같다.

펑터의 법칙을 모두 만족하면 map 함수를 호출했을 때 매핑하는 동작 외에 어떤 것도 하지 않는다는 것을 알 수 있다. 이러한 예측 가능성은 함수가 안정적으로 동작할 뿐만 아니라 더 추상적인 코드로 확장할 때도 도움이 된다.

코틀린으로 배우는 함수형 프로그래밍 7장

profile
공부하는 개발자

0개의 댓글