Pergunta

Eu tenho uma função:

test :: String -> State String String
test x = 
    get >>= \test ->
    let test' = x ++ test in
    put test' >>
    get >>= \test2 -> put (test2 ++ x) >>
    return "test"

Eu posso muito bem compreender o que se passa ao longo desta função, e eu estou começando a pegar o jeito de mônadas. O que eu não entendo é como, quando eu executar este:

runState (test "testy") "testtest"

a função 'get' em 'teste' de alguma forma obtém o estado inicial "testtest". Alguém pode decompô-lo e explicar-me?

Eu aprecio todas as respostas!

Foi útil?

Solução

Eu estava originalmente indo para postar isso como um comentário, mas decidiu expor um pouco mais.

A rigor, get não "tomar" um argumento. Eu acho que muito do que está acontecendo é mascarado por aquilo que você não está vendo - as definições de instância da mônada State

.

get é na verdade um método da classe MonadState. A Mônada Estado é uma instância de MonadState, proporcionando a seguinte definição de get:

get = State $ \s -> (s,s)

Em outras palavras, get apenas retorna uma mônada muito básico Estado (lembrando que uma mônada pode ser pensado como um "wrapper" para um cálculo), onde qualquer s entrada para o cálculo irá retornar um par de s como resultado .

A próxima coisa que precisamos é olhar para >>=, o Estado define assim:

m >>= k  = State $ \s -> let
    (a, s') = runState m s
    in runState (k a) s'

Assim, >>= vai produzir um novo cálculo, que não serão computados até que ele recebe um estado inicial (isto é verdade para todos os cálculos de Estado quando estão em sua forma "embrulhado"). O resultado deste novo cálculo é conseguido através da aplicação de tudo o que está no lado direito da >>= para o resultado da execução do cálculo que estava do lado esquerdo. (Isso é uma frase bastante confuso que pode exigir uma leitura adicional ou dois.)

Eu achei bastante útil para tudo "desugar" que está acontecendo. Se o fizer, preciso muito mais de digitação, mas deve fazer a resposta à sua pergunta (onde get está recebendo a partir de) muito clara. Note-se que o seguinte deve ser considerado psuedocode ...

test x =
    State $ \s -> let
        (a,s') = runState (State (\s -> (s,s))) s  --substituting above defn. of 'get'
        in runState (rightSide a) s'
        where 
          rightSide test = 
            let test' = x ++ test in
            State $ \s2 -> let
            (a2, s2') = runState (State $ \_ -> ((), test')) s2  -- defn. of 'put'
            in runState (rightSide2 a2) s2'
          rightSide2 _ =
            -- etc...

Isso deve tornar óbvio que o resultado final da nossa função é um novo cálculo Estado que terá um valor inicial (s) para fazer o resto das coisas acontecer. Você forneceu s como "testtest" com a sua chamada runState. Se você substituir "testtest" para s no pseudo-código acima, você verá que a primeira coisa que acontece é que corremos get com "testtest" como o 'estado inicial'. Este rendimentos ("testtest", "testtest") e assim por diante.

Assim que é onde get recebe o seu estado inicial "testtest". Espero que isso ajude!

Outras dicas

Pode ajudá-lo a tomar um olhar mais profundo que o tipo de construtor State realmente é, e como RunState usa-lo. Em GHCi:

Prelude Control.Monad.State> :i State
newtype State s a = State {runState :: s -> (a, s)}
Prelude Control.Monad.State> :t runState
runState :: State s a -> s -> (a, s)

State leva dois argumentos: o tipo do estado, e o tipo retornado. Ele é implementado como uma função de tomar o estado inicial e produzindo um valor de retorno e o novo estado.

runState leva função tal, a entrada inicial, e (provavelmente) apenas aplica um para o outro para recuperar o par (resultado, estado).

A sua função test é uma grande composição de funções do tipo State, cada um tomando uma entrada de estado e gerando um (resultado, estado) de saída, ligado um ao outro de uma maneira que faz sentido para o seu programa. Todos runState faz é fornecer-lhes um ponto de partida estado.

Neste contexto, get é simplesmente uma função que leva estado como uma entrada, e retorna um (resultado, estado) de saída de tal modo que o resultado é o estado de entrada, e o estado é inalterada (o estado de saída é o estado de entrada ). Em outras palavras, get s = (s, s)

Indo até o capítulo 8 ( "Analisadores funcionais") de Programação de Graham Hutton em Haskell várias vezes até que eu entendi corretamente, seguido por um ir para o Tudo sobre Monads , feito isso clique para mim.

O problema com monads é que eles são muito úteis para várias coisas que aqueles de nós que vem do fundo de programação habitual encontrar bastante diferentes. Leva algum tempo para perceber que o fluxo de controle e manipulação de estado são suficientes não só semelhantes que podem ser tratadas pelo mesmo mecanismo, mas é quando você voltar longe o suficiente, a mesma coisa.

Uma epifania veio quando eu estava pensando em estruturas de controle em C (for e while, etc.), e eu percebi que, de longe, a estrutura de controle mais comum foi simplesmente colocar uma declaração perante o outro. Demorou um ano de estudo Haskell antes de eu percebi que mesmo que era uma estrutura de controle.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top