2017-04-05 9 views
2

하스켈에서 상태 모나드는 유용 할 수 있습니다. 왜냐하면 우리가 IO 모나드에 있지 않는 한 가변 변수가 없기 때문입니다.스테이트 모나드는 변경 가능 (지역 변수)이있는 언어 (예 : Scala)에서 필요/유용합니까?

그러나 스칼라와 어떤 관계가 있습니까? State Monad는 가변 변수가있는 언어에서 유용합니까?

어떤 의미에서는 모든 주 Monad는 use some local mutable variables within the Monad context을 허용합니다. 예를 들면 다음과 같습니다.

newtype Labeled anytype = Labeled (S -> (S, anytype)) 

instance Monad Labeled where 

    return contents = Labeled (\st -> (st, contents)) 

    Labeled fst0 >>= fany1 = 
    Labeled $ \st0 -> 
     let (st1, any1) = fst0 st0 
      Labeled fst1 = fany1 any1 
     in fst1 st1 

mlabel :: Tr anytype -> Lt anytype 
mlabel tr = let Labeled mt = mkm tr 
      in snd (mt 0) 

mkm :: Tr anytype -> Labeled (Lt anytype) 

mkm (Lf x) 
    = updateState >>= \n -> return $ Lf (n,x) 

mkm (Br l r) 
    = mkm l >>= \l' -> 
    mkm r >>= \r' -> 
    return $ (Br l' r') 

updateState :: Labeled S 
updateState = Labeled (\n -> ((n+1),n)) 

main = print $ mlabel tr1 

이 코드는 스칼라에서 가변 변수를 사용하면 훨씬 간단합니다 (3-4 줄). 비슷해 :

def labelTree = (... recursive call ...)

case class Tr (...)

case class LTr (...)

labelTree 라벨의 현재 상태를 저장하는 가변 로컬 변수를 사용한다.

저는 스칼라에서 State Monads의 유용성을 실제로 볼 수 없습니다. 왜 스칼라에서 State Monad를 사용합니까? 거기에 좋은 유스 케이스가 있습니까?

stateful 계산이 복잡하고 여러 State Monads로 구성되어있는 경우를 상상할 수있는 유일한 유스 케이스이지만 일반적인 모범 함수 프로그래밍과는 달리 상태 모나드를 사용하는 것이 얼마나 유용한 지 잘 모르겠습니다. 인수 전달 (State Monad의 암시 적 인수 전달 대신).

+3

이것은 아마도 의견의 문제 일 것입니다. 그러나 생각을위한 음식 : 하스켈은 가변 참조 (IORef, STRef 등)를 할 수 있으므로 스칼라에서 주를 사용하지 않는 이유는 하스켈에게도 마찬가지로 유효합니다. 왜 하스켈러는 국가를 사용합니까? – user2297560

+0

변경 가능성이있는 언어에서는 그리 유용하지 않다고 생각합니다. 어쩌면 하나의 상태 모나드를 사용하여 특정 서브 루틴이 몇 가지 변수의 상태 만 변이하지만 나머지는 그대로 둡니다. 아마도 상태 모나드가 함수로 구현 된 경우에는 특히 노력이 필요하지 않으므로 일반 변경 가능 변수보다 효율적이지 않습니다 (적어도 최적화가없는 경우). – chi

+2

@ user2297560 어쩌면 그들은 IO Monad에 머물고 싶지 않을 것입니다. 어떤 이유로? – jhegedus

답변

3

변경 가능한 변수 (즉, 프로그래밍 프로그래밍 사고 방식)에서 상태 모나드의 큰 장점은 참조 투명성입니다.

상태 모나드를 사용하는 코드 조각은 var을 사용하여 단위 테스트 가능하고 유지 관리가 용이하며 오류가 발생하기 쉬운 코드입니다. 왜냐하면 코드의 논리는이 코드가 실행되는 위치와 시간에 의존하지 않기 때문입니다 .

같은 문제에 대한 또 다른 관점은 상태 모나드가 값 유형이 변경 가능하다는 사실을 유형 시스템에서 인코딩하는 반면 var은 그러한 정보를 제공하지 않는다는 것입니다. 그것이 작성된 코드와 컴파일 된 출력 사이의 추상화의 다른 수준을 추가하기 때문에

EDIT 그러나, 상태 모나드는 항상 아마 가변 변수보다 효율적이다. 그러나 이는 대부분의 기능적 구성 요소에 대해 말할 수 있으므로 기능적 스칼라 코드에서 var을 사용하는 것은 실제적인 논거가 아닙니다.

+1

그렇지만 'vars'가 ** 로컬로 ** 사용되는 경우 (State Monad에 있음)? 함수 안에서? 위의 예와 같이? 상태 모나드처럼 상태를 삽입하고 (지역 변수를 사용하는) 함수에서 상태를 추출 할 수 있으므로 로컬 솔루션과 마찬가지로 테스트 가능/유지 보수 가능/etc입니다. 상태가 숨겨진 함수는 상태 모나드처럼 명확하게 투명합니다. 왜냐하면 "절반 평가 된"함수를 전달할 수 없기 때문입니다. – jhegedus

+0

그게 훨씬 더 독창적 인 문제가되고있어, 이것과 비슷하다. http://stackoverflow.com/questions/42995875/where-should-one-draw-the-line-between-functional-programming-and-imperative-pro –

+1

"항상 덜 효율적"이라고 나는 확신 할 수 없다. 나는 성능의 대부분을 복구하면서, 가변성을 사용하여 스칼라 상태 모나드를 구현할 수 있다고 생각합니다. – chi

5

하스켈에서 상태 저장 작업을 작성하는 경우 작업을 수행하는 데 필요한 최소한의 상태 만 알 필요가있는 숙어가 있으며 각 작업은 복합 "전역 상태"로 zoomed입니다.예를 들어 :

import Control.Lens 
import Control.Monad.Trans.State 

succInt :: StateT Int IO String 
succInt = do 
    modify succ 
    return "Incr an Int!" 

succChar :: StateT Char IO String 
succChar = do 
    modify succ 
    return "Incr a Char!" 

type GlobalState = (Char,[Int]) 

composite :: StateT GlobalState IO (String,String) 
composite = do 
    r1 <- zoom _1 succChar 
    r2 <- zoom (_2.traversed) succInt 
    return (r1,r2) 

우리는 다음과 같은 예제를 실행하면 :

ghci> runStateT composite ('a',[1,5,7]) 

결과는

(("Incr a Char!","Incr an Int!Incr an Int!Incr an Int!"),('b',[2,6,8])) 

내가 더 큰 상태로 상태의 "확대"가 더있을 거라고 생각입니다 가변 변수로 달성하기 어렵다.

+0

말하자면, "monad-unlift-ref"패키지는 하스켈에서 참조 기반 상태 모나드를 제공한다. http://hackage.haskell.org/package/monad- unlift-ref – danidiaz

+0

답변 주셔서 감사합니다. 이것은 좋은 물건입니다. – jhegedus