Bind () 대신 Join ()을 사용하는 모나드
모나드는 일반적으로 return
및 bind
. 그러나 나는 당신 bind
이 join
(그리고 fmap
?)
일류 기능이 부족한 프로그래밍 언어 bind
에서 사용하기 매우 어색합니다. join
반면에는 아주 쉽게 보입니다.
join
그러나 작동 방식을 완전히 이해하지는 못합니다 . 분명히 [Haskell] 유형이 있습니다.
조인 :: Monad m => m (mx)-> mx
목록 모나드의 경우 이것은 사소하고 분명 concat
합니다. 그러나 일반적인 모나드의 경우이 방법이 실제로 어떤 역할을합니까? 타입 시그니처에 어떤 역할을하는지 알지만 Java 또는 이와 유사한 방식으로 이와 같은 내용을 작성하는 방법을 알아 내려고합니다.
(사실은 간단합니다 : 그렇지 않습니다. 제네릭이 깨 졌기 때문입니다. ;-)하지만 원칙적으로 문제는 여전히 남아 있습니다 ...)
죄송합니다. 이전에 요청한 것 같습니다.
누군가 return
, fmap
및 사용하여 일반적인 모나드의 일부 구현을 스케치 할 수 join
있습니까? (즉, 전혀 언급하지 않습니다 >>=
.) 아마도 그것이 내 멍청한 두뇌에 가라 앉는 데 도움이 될 것 같습니다 ...
은유의 깊이를 연결하지 않고, 전형적인 모나드 m
를 "생산 전략" 으로 읽는 것을 제안 할 수 있습니다 . 그래서 유형 m value
은 "값을 생산하는 전략"의 일급입니다. 다른 계산 개념이나 외부 상호 작용에는 다른 유형의 전략이 필요하지만 일반적인 개념에는 이해하기 위해 규칙적인 구조가 필요합니다.
- 당신이 이미 가치를 가지고 있다면, 당신은 당신이 가지고있는 가치를 생산하는
return :: v -> m v
것 외에는 아무것도없는 가치 ( ) 를 생산하는 전략을 가지고 있습니다. - 한 종류의 가치를 다른 가치로 바꾸는 기능이 있다면
fmap :: (v -> u) -> m v -> m u
, 전략이 가치를 전달하기를 기다렸다가 변형하는 것만으로도 전략 ( )으로 끌어 올릴 수 있습니다 . - 값을 생성하는 전략을 생성하는 전략이있는
join :: m (m v) -> m v
경우 내부 전략을 생성 할 때까지 외부 전략을 따르는 값 ( )을 생성하는 전략을 구성한 다음 해당 내부 전략을 값까지 계속 따라갈 수 있습니다.
예를 들어 보자 : 잎 레이블이 붙은 이진 트리 ...
data Tree v = Leaf v | Node (Tree v) (Tree v)
... 동전을 던져 물건을 생산하는 전략을 나타냅니다. 전략이 Leaf v
이면 귀하의 v
; 전략이 Node h t
이면 동전을 던지고 h
동전에 "앞면"이 표시되고 t
"꼬리"이면 전략을 계속합니다 .
instance Monad Tree where
return = Leaf
전략 생산 전략은 나무 라벨이 붙은 잎이있는 나무입니다. 각 잎 대신 우리는 라벨을 붙인 나무에 접목 할 수 있습니다.
join (Leaf tree) = tree
join (Node h t) = Node (join h) (join t)
... 물론 fmap
나뭇잎의 레이블을 다시 지정합니다.
instance Functor Tree where
fmap f (Leaf x) = Leaf (f x)
fmap f (Node h t) = Node (fmap f h) (fmap f t)
다음은 Int
.
동전 던지기 : "앞면"이라면 다른 동전을 던져 두 전략 사이에서 결정합니다 (각각 "0을 생산하거나 1을 생산하기 위해 동전을 던지십시오"또는 "2를 생산하십시오"). 만약 그것이 "꼬리"라면 1/3을 생산합니다 ( "3을 생산하기 위해 동전을 던지거나 4 또는 5를 위해 동전을 던져").
그건 분명히 join
전략은을 생산하기 위해 최대이야 Int
.
우리가 활용하고있는 것은 "가치를 창출하는 전략"자체가 가치로 보일 수 있다는 사실입니다. Haskell에서 전략을 가치로 포함하는 것은 침묵하지만 영어에서는 전략을 사용하는 것과 단순히 말하는 것과 구별하기 위해 따옴표를 사용합니다. join
운영자는 전략 "당신이 경우"어떻게 든 전략을 따릅니다 생산 ", 또는 표현 말했다 전략을, 당신은 할 수 있습니다 사용 을".
(메타.이 "전략"접근 방식이 모나드와 값 / 계산 구분에 대해 생각하는 적절한 일반적인 방법인지 또는 그저 또 다른 비열한 은유인지 잘 모르겠습니다. 잎 레이블이있는 나무와 같은 유형이 유용하다고 생각합니다. 그것은 아마도 모나드가되기에 충분한 구조를 가진 자유로운 모나드 이기 때문에 놀랄 일이 아닐 것 입니다.
PS "바인드"유형
(>>=) :: m v -> (v -> m w) -> m w
"만약 당신이를 생산할 전략이 있고를 생산하기 v
위한 각 후속 전략에 대해를 생산할 w
전략이 있다면 w
"라고 말합니다. 그것을 어떻게 포착 할 수 join
있습니까?
mv >>= v2mw = join (fmap v2mw mv)
우리는 우리의 레이블을 다시 지정 수 v
로 -producing 전략을 v2mw
각 대신 생산, v
값 w
그것에서에 다음과 -producing 전략 - 준비 join
!
join = concat -- []
join f = \x -> f x x -- (e ->)
join f = \s -> let (f', s') = f s in f' s' -- State
join (Just (Just a)) = Just a; join _ = Nothing -- Maybe
join (Identity (Identity a)) = Identity a -- Identity
join (Right (Right a)) = Right a; join (Right (Left e)) = Left e;
join (Left e) = Left e -- Either
join ((a, m), m') = (a, m' `mappend` m) -- Writer
-- N.B. there is a non-newtype-wrapped Monad instance for tuples that
-- behaves like the Writer instance, but with the tuple order swapped
join f = \k -> f (\f' -> f' k) -- Cont
좋아요, 그래서 당신 자신의 질문에 답하기에는 좋은 형식은 아니지만, 다른 사람을 깨우는 경우를 대비하여 제 생각을 적어 두겠습니다. (나는 의심한다 ...)
모나드가 "컨테이너"로 생각 될 수 있다면 둘 다 return
및 join
꽤 분명한 의미를 갖습니다. return
1 요소 컨테이너를 생성하고 join
컨테이너 컨테이너를 단일 컨테이너로 변환합니다. 그것에 대해 어려운 것은 없습니다.
그래서 좀 더 자연스럽게 "액션"으로 생각되는 모나드에 초점을 맞 춥니 다. 이 경우 "실행"할 때 m x
유형 값을 생성하는 일종의 작업입니다 x
. return x
특별한 작업을 수행하지 않고 x
. fmap f
를 생성하는 x
작업을 수행하고 계산 x
한 다음 적용 하는 작업을 구성 f
하고 결과를 반환합니다. 여태까지는 그런대로 잘됐다.
f
자체적으로 액션을 생성하는 경우 최종 결과는 m (m x)
. 즉, 다른 작업을 계산하는 작업입니다. 어떤면에서 그것은 >>=
행동을 취하는 기능과 "행동을 생성하는 기능"등등 보다 당신의 마음을 감싸는 것이 더 간단 할 것 입니다.
따라서 논리적으로 말하면 join
첫 번째 작업을 실행하고 생성 한 작업을 수행 한 다음 실행합니다. (또는 join
머리카락을 분할하려는 경우 방금 설명한 작업을 수행하는 작업을 반환합니다.)
이것이 핵심 아이디어 인 것 같습니다. 를 구현하려면 join
작업을 실행 한 다음 다른 작업을 제공 한 다음이를 실행하려고합니다. (이 특정 모나드에 대해 "실행"이 의미하는 것이 무엇이든간에.)
이 통찰력이 주어지면 몇 가지 join
구현 을 작성할 수 있습니다 .
join Nothing = Nothing
join (Just mx) = mx
외부 작업이 Nothing
이면 return Nothing
이고 그렇지 않으면 내부 작업을 반환합니다. 다시 말하지만, Maybe
액션 이라기보다는 컨테이너에 가깝기 때문에 다른 것을 시도해 봅시다 ...
newtype Reader s x = Reader (s -> x)
join (Reader f) = Reader (\ s -> let Reader g = f s in g s)
That was... painless. A Reader
is really just a function that takes a global state and only then returns its result. So to unstack, you apply the global state to the outer action, which returns a new Reader
. You then apply the state to this inner function as well.
In a way, it's perhaps easier than the usual way:
Reader f >>= g = Reader (\ s -> let x = f s in g x)
Now, which one is the reader function, and which one is the function that computes the next reader...?
Now let's try the good old State
monad. Here every function takes an initial state as input but also returns a new state along with its output.
data State s x = State (s -> (s, x))
join (State f) = State (\ s0 -> let (s1, State g) = f s0 in g s1)
That wasn't too hard. It's basically run followed by run.
I'm going to stop typing now. Feel free to point out all the glitches and typos in my examples... :-/
I've found many explanations of monads that say "you don't have to know anything about category theory, really, just think of monads as burritos / space suits / whatever".
Really, the article that demystified monads for me just said what categories were, described monads (including join and bind) in terms of categories, and didn't bother with any bogus metaphors:
I think the article is very readable without much math knowledge required.
Calling fmap (f :: a -> m b) (x ::
m
a)
produces values (y ::
m
(m b))
so it is a very natural thing to use join
to get back values (z :: m b)
.
Then bind is defined simply as bind ma f = join (fmap f ma)
, thus achieving the Kleisly compositionality of functions of (:: a -> m b)
variety, which is what it is really all about:
ma `bind` (f >=> g) = (ma `bind` f) `bind` g -- bind = (>>=)
= (`bind` g) . (`bind` f) $ ma
= join . fmap g . join . fmap f $ ma
And so, with flip bind = (=<<)
, we have
((g <=< f) =<<) = (g =<<) . (f =<<) = join . (g <$>) . join . (f <$>)
Asking what a type signature in Haskell does is rather like asking what an interface in Java does.
It, in some literal sense, "doesn't". (Though, of course, you will typically have some sort of purpose associated with it, that's mostly in your mind, and mostly not in the implementation.)
In both cases you are declaring legal sequences of symbols in the language which will be used in later definitions.
Of course, in Java, I suppose you could say that an interface corresponds to a type signature which is going to be implemented literally in the VM. You can get some polymorphism this way -- you can define a name that accepts an interface, and you can provide a different definition for the name which accepts a different interface. Something similar happens in Haskell, where you can provide a declaration for a name which accepts one type and then another declaration for that name which treats a different type.
This is Monad explained in one picture. The 2 functions in the green category are not composable, when being mapped to the blue category (strictly speaking, they are one category), they become composable. Monad is about turning a function of type T -> Monad<U>
into a function of Monad<T> -> Monad<U>
.
참고 URL : https://stackoverflow.com/questions/11234632/monads-with-join-instead-of-bind
'Nice programing' 카테고리의 다른 글
IntelliJ에서 전체 프로젝트에 대해 "단축 명령 줄"방법을 구성하는 방법 (0) | 2020.11.30 |
---|---|
str (변수)가 비어 있는지 확인하는 방법은 무엇입니까? (0) | 2020.11.29 |
Python 2.7의 분할 (0) | 2020.11.29 |
스파크 실행기 번호, 코어 및 실행기 메모리를 조정하는 방법은 무엇입니까? (0) | 2020.11.29 |
Git에서 모든 파일을 수동으로 병합하는 방법은 무엇입니까? (0) | 2020.11.29 |