[Haskell-cafe] Weird defaulting on newEmptyTMVar

Viktor Dukhovni ietf-dane at dukhovni.org
Sun Feb 10 10:07:17 UTC 2019


On Sun, Feb 10, 2019 at 12:24:52AM -0300, Ruben Astudillo wrote:

> Playing on ghci I encountered the following type
> 
>     GHCi, version 8.6.3: http://www.haskell.org/ghc/  :? for help
>     Prelude> :m +Control.Concurrent.STM
>     Prelude Control.Concurrent.STM> var1 <- atomically $ newEmptyTMVar
>     Prelude Control.Concurrent.STM> :t var1
>     var1 :: TMVar GHC.Types.Any
>     Prelude Control.Concurrent.STM>

Much simpler:

    $ ghci
    GHCi, version 8.6.3: http://www.haskell.org/ghc/  :? for help
    Prelude> let v = Nothing
    Prelude> :t v
    v :: Maybe a
    Prelude> v <- return Nothing
    Prelude> :t v
    v :: Maybe GHC.Types.Any

This looks like let-bound polymorphism in action.  But the two types
of "Nothing" are representationally equivalent, so you can write:

    Prelude> :m + Unsafe.Coerce
    ...> case unsafeCoerce v of { Just 1 -> True; _ -> False }
    False
    ...> case unsafeCoerce v of { Just 'a' -> True; _ -> False }
    False

The case of MVars case is more complicated:

    ...> :m + Control.Concurrent.MVar
    ...> v <- newEmptyMVar
    ...> :t v
    v :: MVar GHC.Types.Any
    ...> putMVar (unsafeCoerce v) (1 :: Int)
    ...> x <- readMVar v
    ...> :t x
    x :: GHC.Types.Any
    ...> unsafeCoerce x :: Int
    1

The issues become a bit more clear if we replace the "<-" with
unsafePerformIO:
    
    ...> :m + System.IO.Unsafe
    ...> let v = unsafePerformIO newEmptyMVar
    ...> :t v
    v :: MVar a
    ...> putMVar v (1 :: Int)
    ...> let x = unsafePerformIO (readMVar v)
    ...> :t x
    x :: a

So now we seem to have an "x" that is of any type and yet is not
bottom!  We secretly know it is an Int:

    ...> unsafeCoerce x :: Int
    1

But how is GHC supposed to type check that?  So without opportunities
for type inference, and with no applicable annotations, "Any" seems
quite right, with the only way to get a value out being "unsafeCoerce".

If you want a more friendly empty MVar via the REPL, you need to
add a type annotation:

    ...> v <- newEmptyMVar :: IO (Mvar Int)
    ...> :t v
    v :: MVar Int

In compiled code the MVar's type can often be inferred from the
context in which "v" is used, and the type annotation is then
not required.

-- 
	Viktor.


More information about the Haskell-Cafe mailing list