Haskell에서 <$= (fmap . const) 작동 방식

2024-07-27

fmap 함수 이해:

fmap 함수는 Functor 인스턴스의 값을 함수에 적용하여 새로운 값을 반환하는 함수입니다. 즉, fmap은 기존 값을 함수로 변환하고, 변환된 함수를 다시 값에 적용하여 결과를 도출합니다. 간단히 말해, fmap은 함수를 "맵핑"하는 역할을 합니다.

const 함수 이해:

const 함수는 단 하나의 인수를 받아 그 인수를 그대로 반환하는 함수입니다. 즉, const 함수는 입력값에 어떠한 변화도 일으키지 않고 그대로 유지합니다.

<$= 연산자 작동 방식:

<$= 연산자는 fmap 함수와 const 함수를 조합하여 다음과 같은 작동 방식을 가집니다.

  1. 우측에 있는 값을 const 함수에 적용합니다.
  2. const 함수로부터 반환된 함수를 왼쪽에 있는 Functor 인스턴스의 값에 fmap 함수를 통해 적용합니다.
  3. 최종적으로 fmap 함수의 결과 값을 반환합니다.

예시:

data Maybe a = Nothing | Just a

instance Functor Maybe where
  fmap f (Just x) = Just (f x)
  fmap f Nothing = Nothing

addOne :: Int -> Int
addOne x = x + 1

maybeAddOne :: Maybe Int -> Maybe Int
maybeAddOne = $ const addOne

위 예시에서 maybeAddOne 함수는 Maybe Int 인스턴스를 받아 addOne 함수를 적용합니다. 하지만 addOne 함수는 Int 값만 받아들이기 때문에 Maybe Int 값을 직접 적용할 수 없습니다.

따라서 maybeAddOne 함수는 const addOne을 사용하여 addOne 함수를 상수값으로 변환하고, fmap 함수를 통해 Maybe Int 값에 적용합니다.

만약 Maybe Int 값이 Just x 형태라면 fmap 함수는 addOne x를 계산하여 Just (x + 1) 값을 반환합니다. 반면에 Maybe Int 값이 Nothing 형태라면 fmap 함수는 Nothing을 그대로 반환합니다.




예제 코드: fmap, const, $<= 활용

문자열 리스트 대문자 변환:

data List a = Nil | Cons a (List a)

instance Functor List where
  fmap f Nil = Nil
  fmap f (Cons x xs) = Cons (f x) (fmap f xs)

upper :: Char -> Char
upper c = Char.toUpper c

upperCaseList :: List String -> List String
upperCaseList = $ const upper

이 코드는 List 데이터 타입의 인스턴스에 대한 Functor 클래스 구현과 upperCaseList 함수를 보여줍니다.

  • upperCaseList 함수는 List String 값을 받아 각 문자열을 upper 함수로 변환하여 새로운 List String 값을 반환합니다.
  • $<= 연산자를 사용하여 upper 함수를 상수값으로 변환하고 fmap 함수에 적용합니다.

숫자 리스트 제곱:

square :: Int -> Int
square x = x * x

squaredList :: [Int] -> [Int]
squaredList = $ const square
  • squaredList 함수는 [Int] 값을 받아 각 숫자를 square 함수로 제곱하여 새로운 [Int] 값을 반환합니다.

Maybe 값 증가:

addOne :: Maybe Int -> Maybe Int
addOne = $ const (+1)

이 코드는 Maybe Int 값에 1을 더하는 addOne 함수를 보여줍니다.

  • Maybe Int 값이 Just x 형태라면 fmap 함수는 x + 1을 계산하여 Just (x + 1) 값을 반환합니다.
  • Maybe Int 값이 Nothing 형태라면 fmap 함수는 Nothing을 그대로 반환합니다.

위 예제 코드들은 fmap, const, $<= 연산자를 활용하여 다양한 데이터 변환 작업을 간결하고 효율적으로 수행하는 방법을 보여줍니다.




fmap . const 대신 사용 가능한 방법들

직접적인 fmap 함수 사용:

가장 기본적인 방법은 fmap 함수를 직접 사용하는 것입니다. 이는 명확하고 간결한 코드를 작성하는 데 도움이 될 수 있습니다.

data Maybe a = Nothing | Just a

instance Functor Maybe where
  fmap f (Just x) = Just (f x)
  fmap f Nothing = Nothing

addOne :: Int -> Int
addOne x = x + 1

maybeAddOne :: Maybe Int -> Maybe Int
maybeAddOne x = fmap addOne x

위 예시에서 maybeAddOne 함수는 fmap 함수를 직접 사용하여 Maybe Int 값에 addOne 함수를 적용합니다.

익명 함수 사용:

lambda 표현식을 사용하여 익명 함수를 정의하고 fmap 함수와 함께 사용할 수 있습니다. 이는 간결한 코드를 작성하는 데 도움이 될 수 있으며, 특히 함수가 간단한 경우 유용합니다.

maybeAddOne :: Maybe Int -> Maybe Int
maybeAddOne = fmap (\x -> x + 1)

위 예시에서 maybeAddOne 함수는 익명 함수 (\x -> x + 1)을 사용하여 Maybe Int 값에 1을 더합니다.

liftM 함수 사용:

Control.Monad 모듈에서 제공하는 liftM 함수를 사용할 수 있습니다. 이 함수는 Functor 인스턴스 간 데이터 변환을 위한 더욱 추상적인 방법을 제공하며, 좀 더 복잡한 상황에서 유용할 수 있습니다.

import Control.Monad

maybeAddOne :: Maybe Int -> Maybe Int
maybeAddOne = liftM (+1)

다른 Functor 클래스 인스턴스 활용:

특정 상황에서는 다른 Functor 클래스 인스턴스를 활용하여 더욱 간결하고 효율적인 코드를 작성할 수 있습니다. 예를 들어, Either 타입을 사용하여 오류 처리를 수행할 경우 Either 인스턴스에 대한 Functor 클래스 구현을 활용하는 것이 유용할 수 있습니다.


haskell functor higher-order-functions

haskell functor higher order functions

모나드란 무엇일까요? (하스켈, 함수형 프로그래밍)

모나드의 이해를 돕는 몇 가지 주요 개념:값 포장: 모나드는 값을 포장하여 새로운 값을 생성합니다. 이 새로운 값은 원래 값뿐만 아니라 추가적인 정보나 기능을 포함할 수 있습니다. 예를 들어, Maybe 모나드는 값이 있을 수도 있고 없을 수도 있는 경우를 다루기 위한 모나드입니다


Haskell에서 Monad가 pure를 사용해야 하는 이유

1. 의미론적 명확성:pure 함수는 값 자체를 의미하며, 副作用을 일으키지 않습니다. 이는 코드를 읽고 이해하기 쉽게 만들고, 의도하지 않은 동작을 방지하는 데 도움이 됩니다.2. 추상화:pure 함수는 다양한 타입의 값을 Monad 컨텍스트 안에 통일하여 다루도록 합니다


Haskell에서 GHCi가 클래스 인스턴스를 알려줄 수 있는가?

그 중 하나는 특정 클래스에 대한 인스턴스가 무엇인지 확인하는 것입니다. Haskell은 다형성을 지원하는 언어이기 때문에 클래스를 통해 다양한 유형에 대한 동일한 작업을 수행할 수 있습니다. 예를 들어, Eq 클래스는 두 값이 서로 같은지 비교하는 기능을 제공합니다