es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Por qué no puedo proporcionar una firma de tipo explícita a una acción ST local enlazada con let?

Tengo el siguiente programa simplificado que funciona bien:

{-# LANGUAGE Rank2Types #-}
module Temp where

import Control.Monad.ST
import Control.Monad
import Data.STRef

mystery :: Int -> Int
mystery start =
  let
    run :: ST s Int
    run = do
      count <- newSTRef (1::Int)
      loop count start
      readSTRef count
  in
    runST run

loop :: STRef s Int -> Int -> ST s ()
loop cnt n = when (n /= 1) 
                  (modifySTRef cnt succ >> 
                     if n `mod` 2 == 0 
                       then  loop cnt (n `div` 2) 
                       else  loop cnt (n * 3 + 1))

Muevo la definición de loop dentro del bloque do para poder utilizar el contador creado en run de la siguiente manera:

mystery :: Int -> Int
mystery start =
  let
    run :: ST s Int
    run = do
      count <- newSTRef (1::Int)
      let
        loop :: Int -> ST s ()
        loop n = when (n /= 1) 
                      (modifySTRef count succ >> 
                        if n `mod` 2 == 0 
                          then  loop (n `div` 2) 
                          else  loop (n * 3 + 1))
      loop start
      readSTRef count
  in
    runST run

Esto me da el siguiente error:

No se pudo hacer coincidir el tipo ‘s1’ con ‘s’
    ‘s1’ es una variable de tipo rígida delimitada por
      la firma de tipo de:
        loop :: forall s1. Int -> ST s1 ()
      en ...
    ‘s’ es una variable de tipo rígida delimitada por
      la firma de tipo de:
        run :: forall s. ST s Int
      en ...
    Tipo esperado: ST s1 ()
    Tipo real: ST s ()
    En la expresión:
     ...

Entiendo que s no está permitido escapar, pero según lo que veo, ¿no debería hacerlo? Además, cuando quito la firma de tipo para loop, el problema desaparece. Supongo que eso indica que la firma de tipo es incorrecta de alguna manera, pero es igual que antes, excepto sin el contador y no tengo idea de qué debería ser.

Cambiar el nombre de s para que coincida o no coincida con el s mencionado en run no hace ninguna diferencia.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema con el mensaje de error es que la firma de tipo para loop es demasiado general. Cuantifica sobre todos los posibles tipos de estado s1, pero necesita tener el mismo tipo de estado que run, que está cuantificado en la encuadernación let circundante. La función corregida se vería así:

    mystery :: Int -> Int
    mystery start =
      let
        run :: ST s Int
        run = do
          count <- newSTRef (1::Int)
          let
            loop :: Int -> ST s ()
            loop n = when (n /= 1) 
                          (modifySTRef count succ >> 
                            if n `mod` 2 == 0 
                              then  loop (n `div` 2) 
                              else  loop (n * 3 + 1))
          loop start
          readSTRef count
      in
        runST run
    

    Ten en cuenta que la firma de loop especifica el mismo tipo de estado s que en run, lo que lo convierte en una encuadernación monomórfica. Esta versión corregida debería compilarse sin errores.

Comments are closed.