왜 적용 가능한가?

15274 단어 haskellfunctional
Haskell의 다른 유형 클래스에 대해 배울 때 가장 힘들었던 것은 Applicative 입니다.

펑터



Functor은 적어도 어느 정도는 간단합니다. 우리는 단항 함수를 취하여 일부 (펑터) 컨텍스트에서 작동하도록 만듭니다.

fmap :: Functor f => (a -> b) -> f a -> f b

-- or the infix version
(<$>) :: Functor f => (a -> b) -> f a -> f b
Int s에서 작동하는 증가 함수가 있다고 가정해 보겠습니다.

inc :: Int -> Int



λ> inc 1
2
fmap 를 사용하여 Int 를 포함하는 펑터를 매핑할 수 있습니다.

λ> fmap inc [1, 2, 3]
[2, 3, 4]
λ> fmap inc (Just 1)
Just 2
λ> fmap inc (Right 1)
Right 2

함수 응용 연산자와 정렬fmap하면 매우 명확해집니다.

($) ::                (a -> b) ->   a ->   b
(<$>) :: Functor f => (a -> b) -> f a -> f b

inc  $   1       --  2
inc <$> [1]      -- [2]
inc <$> (Just 1) -- Just 2
fmap은 컨텍스트 내의 함수 응용 프로그램입니다.



그건 그렇고, 우리는 지금부터 infix 버전을 사용할 것입니다.

적용



적용의 경우 명확하지 않습니다. 아니면 적어도 클릭하는 데 더 오래 걸렸습니다.

(<*>) :: Applicative f => f (a -> b) -> f a -> f b
($)와 비교하는 것은 실제로 도움이 되지 않습니다. 컨텍스트에서 기능을 갖고 싶은 이유는 무엇입니까?

($) ::                      (a -> b) ->   a ->   b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

우리는 펑터를 사용하여 컨텍스트에서 단항 함수를 적용할 수 있다고 말했습니다. 그러나 더 높은 arity를 ​​가진 함수를 적용하려면 어떻게 됩니까?

add :: Int -> Int -> Int

add <$> (Just 1) -- ??

담당자가 뭐라고 합니까? 🦊

λ> :t add <$> (Just 1)
add <$> (Just 1) :: Maybe (Int -> Int)
Maybe (Int -> Int) ? 예, 우리는 이미 (<*>) 서명에서 그것을 보았습니다.

add <$> (Just 1) <*> (Just 2) -- Just 3

🔍 해부해보자

-- refresh these ones first :)
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

-- With Maybe applied (using TypeApplications extension)

(<$>) @Maybe :: (a -> b) -> Maybe a -> Maybe b

add :: Int -> Int -> Int

-- With Int applied in place of 'a'
(<$>) @Maybe @Int :: (Int -> b) -> Maybe Int -> Maybe b

-- With (Int -> Int) applied in place of 'b'
(<$>) @Maybe @Int @(Int -> Int)
--   (a   -> b)          -> Maybe a   -> Maybe b
  :: (Int -> Int -> Int) -> Maybe Int -> Maybe (Int -> Int)

add <$> (Just 1) :: Maybe (Int -> Int)

여기서 우리는 첫 번째 흥미로운 것을 봅니다. add 함수는 두 개의 인수를 취하기 때문에(또는 한 번에 하나씩 더 정확하게). 하지만 Int 에서 Maybe Int 하나만 제공하므로 부분적으로 적용되고 함수( Int -> Int )를 반환합니다. 따라서 bInt -> Int 입니다.

--     a  ->   b 
add :: Int -> (Int -> Int)

화살표( -> )가 오른쪽 결합이므로 괄호는 실제로 필요하지 않습니다.

그것이 표현의 첫 부분이었다. 우리는 적용을 놓치고 있습니다.

(<*>) @Maybe :: Maybe (c -> d) -> Maybe c -> Maybe d

-- With Int in place of 'c'
(<*>) @Maybe @Int :: Maybe (Int -> b) -> Maybe Int -> Maybe b

-- And also Int in place of 'd'
(<*>) @Maybe @Int @Int
  :: Maybe (Int -> Int) -> Maybe Int -> Maybe Int

엣 짜잔

add :: Int -> Int -> Int
add <$> (Just 1) :: Maybe (Int -> Int)
add <$> (Just 1) <*> (Just 2) :: Maybe  Int

Functor: 컨텍스트에서 단항 함수를 적용합니다.

적용: 컨텍스트에서 n-항 함수를 적용합니다.

이것을 Haskell에서는 lift이라고 합니다.

그리고 그러한 맥락에서 기능을 적용하는 요점은 그것들과 관련된 의미론입니다. 유효성 검사, 선택적 값(null 😏 제외), 항목 목록 또는 트리, 실행 중인 IO 작업, 파서를 위한 것일 수 있습니다.

컨텍스트가 Maybe일 때:

λ> add <$> (Just 1) <*> (Just 2)
Just 3
λ> add <$> Nothing <*> (Just 2)
Nothing
λ> add <$> (Just 1) <*> Nothing
Nothing
λ> add <$> Nothing <*> Nothing
Nothing

컨텍스트가 다음 중 하나일 때:

λ> add <$> (Right 1) <*> (Right 2)
Right 3
λ> add <$> (Left "err 1") <*> (Right 2)
Left "err 1"
λ> add <$> (Right 1) <*> (Left "err 2")
Left "err 2"
λ> add <$> (Left "err 1") <*> (Left "err 2")
Left "err 1"

컨텍스트가 목록인 경우:

λ> add <$> [1, 2, 3] <*> [1, 2, 3]
[2,3,4,3,4,5,4,5,6]
λ> (,) <$> [1, 2, 3] <*> [1, 2, 3]
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)]

☝️ 자세한 내용은 다음 시간에.

결론



함수형 프로그래밍을 배울 때 이러한 모든 유형 클래스가 무섭게 보일 수 있습니다. 그것들의 목적과 사용법에 대한 기본적인 직관을 개발하는 것은 그것들을 사용하는 데 익숙해지는(그리고 이해하는) 과정의 큰 부분입니다.

아직 발견하지 못한 Functor와 Applicative에 더 많은 의미가 있다는 것을 알고 있습니다. 그러나 모든 학습 과정과 마찬가지로 시간이 걸립니다. 계속 하다 보면 더 많은 것들이 명확해지고 깊이 들어가기 시작할 것이라고 확신합니다.

하지만 오늘은 여기까지입니다.

즐겁고 안전한 코딩 🎉

좋은 웹페이지 즐겨찾기