함수형 프로그래밍에서 응용 함수를 사용해야하는 이유는 무엇입니까?


78

저는 Haskell을 처음 접했고 펑터와 응용 펑터에 대해 읽고 있습니다. 좋아, 나는 펑터와 그것들을 어떻게 사용할 수 있는지 이해하지만, 응용 펑터가 왜 유용한 지, 하스켈에서 어떻게 사용할 수 있는지 이해하지 못합니다 . 응용 펑터가 필요한 이유를 간단한 예를 들어 설명해 주시겠습니까?


내가 줄 수있는 링크 일 뿐이지 만 여기 에 예제와 함께 Applicative Functor에 대한 멋진 설명이 있습니다.
Hartmut P. 2011

답변:


56

응용 펑 터는 펑터와 모나드 사이의 중간 점을 제공하는 구조 이므로 모나드보다 더 널리 퍼져 있지만 펑터보다 더 유용합니다. 일반적으로 펑터 위에 함수를 매핑 할 수 있습니다. Applicative functor를 사용하면 "일반"함수 (비 기능적 인수 사용)를 사용하여 펑터 컨텍스트에있는 여러 값에 대해 작업 할 수 있습니다. 결과적으로 이것은 모나드없이 효과적인 프로그래밍을 제공합니다.

여기 에서 예제로 가득 찬 멋진 독립적 인 설명을 찾을 수 있습니다 . 사전 지식이 필요하지 않은 Bryan O'Sullivan이 개발 한 실제 구문 분석 예제를 읽을 수도 있습니다 .


2
유용한 링크가 있지만 질문에 대답하는 데 초점을 맞춘 간단한 간단한 예제를 가능한 한 많이 제거하여 모든 중요하지 않은 세부 사항을 대체 할 수는 없다고 생각합니다.
Dmitri Zaitsev

34

적용 펑 터는 일련의 작업이 필요할 때 유용하지만 중간 결과의 이름을 지정할 필요는 없습니다. 따라서 모나드보다 약하지만 펑터보다 강력합니다 (명시적인 바인드 연산자가 없지만 펑터 내에서 임의의 함수를 실행할 수 있음).

언제 유용합니까? 일반적인 예는 데이터 구조의 일부를 순서대로 읽는 여러 작업을 실행 한 다음 모든 결과를 함께 붙여야하는 구문 분석입니다. 이것은 일반적인 형태의 함수 구성과 같습니다.

f a b c d

a, b등을 실행할 임의의 동작으로 생각할 수 f있고 결과에 적용 할 펑터로 생각할 수 있습니다.

f <$> a <*> b <*> c <*> d

나는 그것들을 과부하 된 '공백'으로 생각하고 싶다. 또는 일반 Haskell 함수는 ID 적용 펑터에 있습니다.

"을 참조하십시오 실용적이 효과로 프로그래밍 "


12

Conor McBride와 Ross Paterson의 스타일에 대한 Functional Pearl 에는 몇 가지 좋은 예가 있습니다. 또한 애초에 스타일을 대중화하는 역할을합니다. 그들은 "applicative functor"에 "idiom"이라는 용어를 사용하지만 그 외에는 꽤 이해할 수 있습니다.


8

응용 펑터 가 필요한 예를 찾기 가 어렵습니다 . 대부분의 입문 텍스트는 Applicative Functors를 편리한 인터페이스로만 사용하여 모나드에서 파생 된 인스턴스를 제시하기 때문에 중급 Haskell 프로그래머가 스스로 그 질문을하는 이유를 이해할 수 있습니다.

여기와 주제에 대한 대부분의 소개에서 언급했듯이 주요 통찰은 Applicative Functor가 Functor와 Monads 사이에 있다는 것입니다 (Functor와 Arrows 사이에서도). 모든 모나드는 Applicative Functor이지만 모든 Functor가 Applicative는 아닙니다.

그래서 필연적으로, 때때로 우리는 모나 딕 결합자를 사용할 수없는 것에 적용 결합자를 사용할 수 있습니다. 그러한 것 중 하나는 ZipList(자세한 내용은 이 SO 질문 참조 ), 이는 목록 의 Monad 인스턴스에서 파생 된 것과 다른 Applicative 인스턴스 를 갖기 위해 목록을 둘러싼 래퍼 입니다. Applicative 문서는 다음에 대한 직관적 인 개념을 제공하기 위해 다음 행을 사용합니다 ZipList.

f <$> ZipList xs1 <*> ... <*> ZipList xsn = ZipList (zipWithn f xs1 ... xsn)

여기서 지적했듯이 ZipList에서 거의 작동하는 기발한 Monad 인스턴스를 만드는 것이 가능합니다.

모나드가 아닌 다른 Applicative Functor ( SO 질문 참조 )가 있으며 쉽게 생각 해낼 수 있습니다 . 모나드에 대한 대체 인터페이스를 갖는 것은 훌륭하지만 때로는 모나드를 만드는 것이 비효율적이거나 복잡하거나 심지어 불가능 하기도합니다. 바로 이때 응용 펑터 가 필요합니다 .


면책 조항 : Applicative Functor를 만드는 것은 비효율적이고 복잡하며 불가능할 수도 있습니다. 확실하지 않은 경우 Applicative Functor의 올바른 사용을 위해 지역 범주 이론가에게 문의하십시오.


7

내 경험상 Applicative functor는 다음과 같은 이유로 훌륭합니다.

특정 종류의 데이터 구조는 강력한 구성 유형을 허용하지만 실제로 모나드로 만들 수는 없습니다. 사실 함수형 리 액티브 프로그래밍의 추상화는 대부분이 범주에 속합니다. 기술적으로는 예를 들어 Behavior(일명 Signal) 모나드 를 만들 수 있지만 일반적으로 효율적으로 수행 할 수 없습니다. Applicative functor를 사용하면 효율성을 희생하지 않고도 강력한 구성을 유지할 수 있습니다.

응용 펑터에 데이터 의존성이 없기 때문에 예를 들어 사용할 수있는 데이터 없이도 생성 할 수있는 모든 효과를 찾는 작업을 순회 할 수 있습니다. 따라서 다음과 같이 사용되는 "웹 양식"응용 프로그램을 상상할 수 있습니다.

userData = User <$> field "Name" <*> field "Address"

사용 된 모든 필드를 찾아서 양식에 표시하는 엔진을 작성할 수 있습니다. 그런 다음 데이터를 다시 가져 오면 다시 실행하여 생성 된 User. 이것은 일반 펑터 (두 가지 형태를 하나로 결합하기 때문에) 나 모나드로는 할 수 없습니다. 왜냐하면 모나드로 표현할 수 있기 때문입니다 :

userData = do
    name <- field "Name"
    address <- field $ name ++ "'s address"
    return (User name address)

첫 번째 필드의 응답이 없으면 두 번째 필드의 이름을 알 수 없기 때문에 렌더링 할 수 없습니다. 이 양식 아이디어를 구현하는 라이브러리가 있다고 확신합니다.이 프로젝트와 해당 프로젝트를 위해 몇 번이나 롤링했습니다.

실용적 펑에 대한 또 다른 좋은 일들이 있다는 것입니다 구성 . 보다 정확하게는 컴포지션 펑터 :

newtype Compose f g x = Compose (f (g x))

실용적 때마다 f하고 g있습니다. 일부 불쾌한 방식으로 복잡한 모나드 트랜스포머 스토리 전체를 생성 한 모나드도 마찬가지입니다. 응용 프로그램은 이러한 방식으로 매우 깨끗하며 작은 구성 가능한 구성 요소에 집중하여 필요한 유형의 구조를 구축 할 수 있음을 의미합니다.

최근에 ApplicativeDo확장 기능이 GHC에 등장하여 do모나 디 작업을 수행하지 않는 한 응용 프로그램과 함께 표기법 을 사용 하여 표기법의 복잡성을 완화 할 수 있습니다.


6

한 가지 좋은 예 : 응용 파싱.

[real world haskell] ch16 http://book.realworldhaskell.org/read/using-parsec.html#id652517 참조

다음은 do-notation이있는 파서 코드입니다.

-- file: ch16/FormApp.hs
p_hex :: CharParser () Char
p_hex = do
  char '%'
  a <- hexDigit
  b <- hexDigit
  let ((d, _):_) = readHex [a,b]
  return . toEnum $ d

functor를 사용하면 훨씬 짧아집니다 .

-- file: ch16/FormApp.hs
a_hex = hexify <$> (char '%' *> hexDigit) <*> hexDigit
    where hexify a b = toEnum . fst . head . readHex $ [a,b]

'리프팅'은 일부 반복되는 코드의 기본 세부 정보를 숨길 수 있습니다. 그런 다음 더 적은 단어를 사용하여 정확하고 정확한 이야기를 전달할 수 있습니다.


4
"훨씬 더 짧다"라는 이상한 생각이 들었습니다. 적용 버전이 7 자 더 깁니다!
Daniel Wagner

@Daniel Wagner : -_- ||, 오 마이 .. 당신은 코드에 대한 좋은 통찰력을 가지고 있습니다. 고백해야합니다. 나는 실제로 'vertically
shorter

Control.Monad 라이브러리의 공통 함수를 사용하여 더 짧게 작성할 수 있습니다 char '%' >> liftM (toEnum . fst . head . readHex) (replicateM 2 hexDigit)..
HaskellElephant

또는에서 count콤비 네이터를 사용하고 Parsec적용 가능한 스타일로 다시 전환합니다.toEnum . fst . head . readHex <$> (char '%' >> count 2 hexDigit)
pat

3

나는 또한 살펴볼 것을 제안합니다 이것을

기사의 끝에는 예가 있습니다.

import Control.Applicative
hasCommentA blogComments =
BlogComment <$> lookup "title" blogComments
            <*> lookup "user" blogComments
            <*> lookup "comment" blogComments

응용 프로그래밍 스타일의 몇 가지 기능을 보여줍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.