2011-09-22 2 views
13

최근 몇 번 다음 패턴으로 코드를 작성했으며이를 작성하는 데 더 짧은 방법이 있는지 궁금해하고있었습니다.이와 같은 기능이 이미 있습니까? (또는이 함수의 더 나은 이름은 무엇입니까?)

foo :: IO String 
foo = do 
    x <- getLine 
    putStrLn x >> return x 

는 (잘 모르겠어요하지만 그것은 적절한 이름이다) 나는이 기능을 쓴 것을 조금 청소기를 만들려면 :

constM :: (Monad m) => (a -> m b) -> a -> m a 
constM f a = f a >> return a 

내가 다음 수는 다음과 같이 foo는 :

foo = getLine >>= constM putStrLn 

이와 같은 함수/숙어가 이미 있습니까? 그리고 그렇지 않다면 내 constM에 대한 더 나은 이름은 무엇입니까? 그것은 매우 당신이 요구하는지 아니다

http://hackage.haskell.org/packages/archive/haskell98/latest/doc/html/Prelude.html#v:interact

하지만 유사 :

+4

일반 펑터를 사용하여이 함수를 정의 할 수있을 것 같습니다 :'constF :: Functor f => (a -> f b) -> a -> f a; constF f a = a <$ f a' – fuz

+0

@FUZxxl, 이것도 잘 작동합니다. 덕분에 ... const 이름을 올바르게 사용 했나요? –

+0

무엇을 의미합니까? – fuz

답변

18

글쎄, 과 같은 것이 일 수있는 방법을 고려해 보겠습니다. 비 모나 딕 버전은 const' f a = const a (f a)과 같은 것으로 추측됩니다. 더 구체적인 유형의 flip const과 분명히 같습니다. 그러나 모나드 버전의 경우 의 결과는 a의 값에 의존하는 것을 포함하여 펑터의 비모수 적 구조 (즉, "부작용"이라고도하는)에 대해 임의의 작업을 수행 할 수 있습니다. 이 말은 인 것처럼 보이지만 결과가 f a 인 것처럼 보이지 않습니다. 실제로는 아무 것도하지 않습니다. functor의 파라 메트릭 부분으로 a을 변경하지 않은 것은 더 중요하지 않으며 return을 다른 것으로 대체 할 수 있고 여전히 개념적으로 비슷한 기능을 가지고 있습니다. 여기에서

doBoth :: (Monad m) => (a -> m b) -> (a -> m c) -> a -> m c 
doBoth f g a = f a >> g a 

,의 기본 구조를 찾는 두 가지 방법이 있습니다 :

그래서 우리는 결론을 내릴 수있는 첫 번째 일은이 다음과 같은 기능의 특별한 경우로 볼 수 있다는 것입니다 몇가지 종류.


하나의 관점은 다음 결과 재결합, 분할 다중 기능 중 하나의 인자의 패턴을 인식하는 것이다. 이 때문에 같은 기능의 Applicative/Monad 인스턴스에 의해 구현 된 개념이다 :

doBoth :: (Monad m) => (a -> m b) -> (a -> m c) -> a -> m c 
doBoth f g = (>>) <$> f <*> g 

...또는, 당신이 선호하는 경우 : 물론

doBoth :: (Monad m) => (a -> m b) -> (a -> m c) -> a -> m c 
doBoth = liftA2 (>>) 

, liftA2liftM2에 해당 다른 모나드에 모나드에 대한 조작을 리프팅 모나드 변압기 함께 할 수있는 뭔가가 그래서 만약 당신이 궁금 할 것이다; 일반적 관계에 불편이 있지만,이 경우에는이 같은 것을주고, 쉽게 작동합니다

doBoth :: (Monad m) => ReaderT a m b -> ReaderT a m c -> ReaderT a m c 
doBoth = (>>) 

... 모듈 적절한 포장 등의 과정. 원래 버전으로 되돌리려면 원래 return의 사용은 ReaderT a m a 유형의 코드 여야합니다. 판독기 모나드의 경우 ask 함수로 인식하기가 너무 어려워서는 안됩니다.


다른 관점 (Monad m) => a -> m b같은 유형의 기능이 훨씬 순수 기능처럼 직접 구성 될 수 있다는 것을 인식하는 것입니다. 함수 (<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)은 함수 구성 (.) :: (b -> c) -> (a -> b) -> (a -> c)과 직접적으로 동일하거나 대신 Control.Categorynewtype 래퍼 Kleisli을 사용하여 일반적인 방법으로 동일한 작업을 수행 할 수 있습니다.

우리는 여전히 인수를 분할해야하지만 여기에 실제로 필요한 것은 Category에는없는 분기 구성입니다. 다음과 같이 우리가 기능을 다시시키는뿐만 아니라 우리가 (&&&) 얻을 Control.Arrow를 사용하여 :

doBoth :: (Monad m) => Kleisli m a b -> Kleisli m a c -> Kleisli m a (b, c) 
doBoth f g = f &&& g 

을 우리가 처음 Kleisli 화살표의 결과에 대해 상관하지 않기 때문에, 유일한 부작용은 우리의 절반을 삭제할 수 있습니다 명백한 방법으로 튜플 :

doBoth :: (Monad m) => Kleisli m a b -> Kleisli m a c -> Kleisli m a c 
doBoth f g = f &&& g >>> arr snd 

일반적인 형식으로 우리를 되 찾을 수 있습니다. 원래에 전문의 return는 이제 단순히 id됩니다 : 일반 기능 Arrow의 당신이 유형의 서명을 일반화하면 정의는 위뿐만 아니라이 작품 또한

constKleisli :: (Monad m) => Kleisli m a b -> Kleisli m a a 
constKleisli f = f &&& id >>> arr snd 

입니다. 그러나, 다음과 같이 순수 기능에 대한 결과의 정의를 확장하고 단순화하는 계몽 할 수있다 :

  • \f x -> (f &&& id >>> arr snd) x
  • \f x -> (snd . (\y -> (f y, id y))) x
  • \f x -> (\y -> snd (f y, y)) x
  • \f x -> (\y -> y) x
  • \f x -> x.

예상대로 flip const으로 돌아 왔습니다.

한마디로

, 함수 중 하나 (>>) 또는 flip const에 대한 몇 가지 변화가 있지만 그 차이에 의존하는 방법으로 - ReaderT 환경과 기본 모나드의 (>>) 모두 사용 전, 후자의 사용 특정 Arrow의 암시 적 부작용 및 특정 효과 순서로 Arrow 부작용이 발생할 것으로 예상됩니다. 이러한 세부 사항 때문에 일반화 또는 단순화가 가능하지는 않습니다. 어떤 의미에서는 사용하는 정의가 정확히 필요한만큼 간단하므로 내가 지정한 대체 정의가 더 길거나 줄 바꿈 및 줄 바꿈을 필요로합니다.

이와 같은 기능은 일종의 "모나드 유틸리티 라이브러리"에 자연스럽게 추가됩니다. Control.Monad은 이러한 라인을 따라 일부 결합자를 제공하지만, 포괄적 인 것은 아니므로 표준 라이브러리에서이 함수의 변형을 발견하거나 회수 할 수 없었습니다. 그러나 나는 하나 또는 그 이상의 유틸리티 라이브러리에서 hackage를 발견하게되어 전혀 놀랄 일이 아니다.

대부분 존재 여부에 대한 질문이 없기 때문에, 관련 개념에 대한 위의 논의에서 취할 수있는 것 이상으로 많은 이름을 붙일 수는 없습니다.

마지막으로, 주요 목표가 무엇이든 상관없이 표현식을 실행 한 이후 모나드 표현식의 결과에 따라 함수에 제어 흐름 선택 항목이 없음을 유의하십시오. 매개 변수 내용 (예 : Monad m => m aa 유형의 항목)과 독립적 인 계산 구조를 갖는 것은 일반적으로 Monad이 실제로 필요하지 않으며 더 일반적인 개념 인 Applicative으로 얻을 수 있다는 신호입니다.

+0

그의 질문에 대한 대답이 아닙니다. 어쨌든 훌륭한 분석을 위해 +1하십시오. :-) – luqui

+0

@luqui : 설명 된 * 정확한 * 함수가 존재하지 않는다는 것을 감안할 때, 무언가의 일반화를 찾기 위해 기본 구조를 찾는 일종의 순회적인 설명이되도록 의도되었습니다. 예, 그 일종의 길은 길을 잃었습니다. 좀 더 명확하게하기 위해 이것을 약간 수정해야 할 것입니다. –

0

interact입니다.

+2

그는 또한 결과를 돌려주고 싶습니다. 아마도 그는 단순한 예제로 원하는 기능을 설명하기를 원했을 것입니다.당신의 대답은별로 도움이되지 않습니다. – fuz

1

이 단서는 정확히 존재하지 않지만 다른 이름을 가진 파서 생성기에서 많이 볼 수 있습니다 (예 : 건을 얻기 위해을 대괄호 안에 넣음). 연산자 (예 : >>..>>, 예 : fparsec). 정말로 이름을 부여하려면 을 무시하고을 무시하겠습니까?

+1

'ignore :: Monad m => m a -> m()','ignore m = m >> return()'에 사람들이'ignore'라는 이름을 사용하는 것을 보았습니다. –

+0

@Judah do-statement의 결과를 삭제하는 경고를 표시 하시겠습니까? – fuz

+0

주어진 값을 반환해야하므로 무시가 올바른지 확신 할 수 없습니다. –

3

흠, 여기서는 constM이 적합하지 않다고 생각합니다.

map :: (a -> b) -> [a] -> [b] 
mapM :: (Monad m) => (a -> m b) -> [a] -> m b 

const :: b -> a -> b 

그래서 아마도이 :

constM :: (Monad m) => b -> m a -> m b 
constM b m = m >> return b 

기능 당신은 M -ing가 될 것입니다 :

첫 번째 인자를 무시하지만 선택의 여지가 없습니다
f :: (a -> b) -> a -> a 

. 따라서이 기능은 순전히 말할 필요가 없습니다.

내가보기에는, 의 값은 이고 부작용은입니다. observe, effect, sideEffect은 괜찮은 이름 일 수 있습니다. observe은 내가 좋아하는 것이지만 어쩌면 재미 있기 때문에가 아니라 아마도 그렇습니다.

+0

나는 내 이름보다 관찰이 더 낫다고 생각하며, 이것에 순수한 동등한 의미가 없기 때문에 'M'이 필요하지 않다고 생각한다. –

+0

나는 '관찰하다'에 대해 모호하다. 우리는 확실히 함수에 의해 생성 된 값을 관찰하지 않으며 함수는'Applicative' 만 필요로하기 때문에 반드시 부작용을 관찰 할 필요는 없습니다. –