문제

~ 안에 실제 하스켈, 그들은 다음과 같이 결합자를 설명합니다.

Haskell에서는 다른 함수를 인수로 취하고 새 함수를 결합자로 반환하는 함수를 참조합니다.

그리고 나중에 그들은 이렇게 말합니다. maybeIO 함수는 결합자이며 해당 유형 서명은 다음과 같습니다.

maybeIO :: IO a -> IO (Maybe a)

하지만 내가 볼 수 있는 건 그것 뿐이야 maybeIO IO 모나드에 싸인 값을 가져와 IO 모나드에 값을 반환하는 함수입니다.그렇다면 이 함수는 어떻게 결합자가 되는가?

도움이 되었습니까?

해결책

결합자라고 말할 때 실제로 의미할 수 있는 두 가지가 있습니다.말이 좀 과하네요.

  1. 우리는 일반적으로 사물을 "결합"하는 기능을 의미합니다.예를 들어, 귀하의 함수는 IO 가치를 부여하고 더 복잡한 가치를 구축합니다.이러한 "결합자"를 사용하여 새로운 복합체를 결합하고 생성할 수 있습니다. IO 상대적으로 적은 수의 기본 함수에서 값을 생성 IO 가치.

    예를 들어, 10개의 파일을 읽는 함수를 만드는 대신 다음을 사용합니다. mapM_ readFile.여기서 결합자는 값을 결합하고 구축하는 데 사용하는 함수입니다.

  2. 더 엄격한 컴퓨터 과학 용어는 "자유 변수가 없는 함수"입니다.그래서

     -- The primitive combinators from a famous calculus, SKI calculus.
     id a         = a -- Not technically primitive, genApp const const
     const a b    = a
     genApp x y z = x z (y z)
    

    이는 기본적으로 자유 변수를 제거하고 이를 결합자와 몇 가지 기본 함수로 대체하려는 "조합 논리"라는 더 큰 분야의 일부입니다.

TLDR:일반적으로 연결자(combinator)라고 하면 더 복잡한 값을 구성하기 위한 소수의 기본 함수와 많은 사용자 정의 함수가 있는 "연결자 패턴"이라는 보다 일반적인 개념을 참조합니다.

다른 팁

결합기에 대한 엄격한 정의가 없으므로 그 의미에서는 아무 의미가 없습니다. 그러나 더 단순한 기능에서보다 복잡한 기능이나 가치를 구축하는 것은 매우 일반적이며, 때로는 기능이 완전히 맞지 않기 때문에 우리는 그들을 함께 붙이게하기 위해 일부 접착제를 사용합니다. 우리가 사용하는 접착제 비트는 결합기를 호출합니다.

예를 들어 가장 가까운 정수로 반올림 된 숫자의 제곱근을 계산하려면 해당 함수를

로 쓸 수 있습니다.
approxSqrt x = round (sqrt x)
.

우리가 정말로 여기에서 수행하는 것이 두 가지 기능을 사용하고 빌딩 블록으로 사용하는보다 복잡한 기능을 구축하는 것입니다. 그러나 우리는 그들을 함께 붙이기 위해 어떤 종류의 접착제가 필요하며, 그 접착제는 (.)입니다.

approxSqrt = round . sqrt
.

함수 구성 조작자는 기능의 조합 자이기 때문에 기능을 결합하여 새로운 기능을 만듭니다. 또 다른 예는 아마도 파일의 각 줄을 목록으로 읽을 것입니다. 당신은 이것을 분명한 방법으로 할 수 있습니다 :

do
  contents <- readFile "my_file.txt"
  let messages = lines contents
  ...
.

하지만! 파일을 읽고 문자열로 내용을 반환하는 함수가 있으면 무엇을 할 것입니까? 그런 다음 우리는 할 수 있습니다

do
  messages <- readFileLines "my_file.txt"
  ...
.

밖으로 나올 때 파일을 읽는 기능이 있으며 큰 문자열을 사용하는 함수를 가지고 있고 그 줄의 목록을 반환합니다. 우리가 그 두 가지 기능을 의미있는 방식으로 충족시키기 위해 접착제가있는 경우, 우리는 readFileLines를 빌드 할 수 있습니다! 그러나 물론, 이것은 Haskell이며, 그 접착제가 쉽게 이용 가능합니다.

readFileLines = fmap lines . readFile
.

여기에 우리는 두 가지 결합기를 사용합니다! 우리는 이전에서 (.)를 사용하고 fmap는 실제로 매우 유용한 조합 자입니다. 우리는 IO 모나드로 순수한 계산을 "리프트"하고, 우리가 실제로 의미하는 것은 lines가 서명 유형

lines :: String -> [String]
.

그러나 fmap lines에는 서명이 있습니다

fmap lines :: IO String -> IO [String]
.

그래서 fmap는 순수한 계산을 IO 계산과 결합하려는 경우 유용합니다.


이들은 단순히 두 가지 간단한 예제였습니다. 더 많은 Haskell을 배우면서 더 많은 조합자가 자신에게 더 많은 조합 자를 필요로하는 자신의 조합 자를 찾아보십시오. Haskell은 기능을 취하고 변형, 결합하고, 내부를 밖으로 돌리고, 함께 붙이는 방식으로 매우 강력합니다. 우리가 할 때 때로는 접착제의 비트가 필요할 때 때때로 결합기를 호출합니다.

"Combinator"는 Haskell에서 사용 시 정확하게 정의되어 있지 않습니다.다른 함수를 인수로 취하는 함수를 참조하는 데 사용하는 것이 가장 정확합니다. 결합자 미적분학 하지만 하스켈 용어에서는 "수정"이나 "조합" 기능을 의미하기도 하는 경우가 많습니다. 특히 Functor 또는 Monad.이 경우 결합자는 "컨텍스트에 따라 일부 작업이나 값을 수행하고 컨텍스트에 따라 새로운 수정된 작업이나 값을 반환하는" 함수라고 말할 수 있습니다.

당신의 예, maybeIO 흔히 불린다. optional

optional :: Alternative f => f a -> f (Maybe a)
optional fa = (Just <$> fa) <|> pure Nothing

그리고 계산이 필요하기 때문에 결합자와 같은 성격을 가집니다. f a 그리고 그 가치에 실패를 반영하도록 일반적으로 수정합니다.

이것을 결합자라고 부르는 이유는 사용 방법과 관련이 있습니다.볼 수 있는 대표적인 곳 optional (그리고 실제로, Alternative 일반적으로)은 파서 결합자 라이브러리에 있습니다.여기서는 간단한 방법을 사용하여 기본 파서를 구축하는 경향이 있습니다. Parser마치

satisfy :: (Char -> Bool) -> Parser Char
anyChar    = satisfy (const True)
whitespace = satisfy isSpace
number     = satisfy isNumeric

그런 다음 "결합자"를 사용하여 동작을 "수정"합니다.

-- the many and some combinators
many :: Alternative f => f a -> f [a] -- zero or more successes
some :: Alternative f => f a -> f [a] -- one  or more successes

many f = some f <|> pure []
some f = (:) <$> f <*> many f

-- the void combinator forgets what's inside the functor
void :: Functor f => f a -> f ()
void f = const () <$> f

-- from the external point of view, this is another "basic" Parser
-- ... but we know it's actually built from an even more basic one
-- and the judicious application of a few "combinators"
blankSpace = Parser ()
blankSpace = void (many whitespace)

word :: Parser String
word = many (satisfy $ not . isSpace)

종종 우리는 여러 함수를 결합하는 함수를 호출하기도 합니다.Functors/Monads 아마도 니모닉적인 의미가 있는 "결합자"도 마찬가지입니다.

-- the combine combinator
combine :: Applicative f => f a -> f b -> f (a, b)
combine fa fb = (,) <$> fa <*> fb

-- the ignore-what's-next combinator
(<*) :: Applicative f => f a -> f b -> f a
fa <* fb = const <$> fa <*> fb

-- the do-me-then-forget-me combinator
(*>) :: Applicative f => f a -> f b -> f b
fa *> fb = flip const <$> fa <*> fb

line = Parser String
line = many (satisfy $ \c -> c /= '\n') <* satisfy (=='\n')

그러나 궁극적으로 연결자는 API의 엄격한 표시보다는 API의 의도와 사용법에 더 가깝습니다.함수나 함수와 같은 "기본 부분"으로 구성된 라이브러리를 자주 볼 수 있습니다. satisfy 그런 다음 수정되어 "결합자" 세트와 결합됩니다.그만큼 Parser 위의 예는 전형적인 예이지만 전체적으로 이것은 매우 일반적인 Haskell 패턴입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top