[Haskell-cafe] puzzling polymorphism behavior (7.0.3 windows)

Jake McArthur jake.mcarthur at gmail.com
Fri Mar 16 00:06:29 CET 2012


Just a little more interesting information: This is why impure languages
like OCaml have the value restriction. Haskell doesn't need it because it
is pure, but of course unsafePerformIO thwarts that.
On Mar 15, 2012 1:34 PM, "Tillmann Rendel" <rendel at informatik.uni-marburg.de>
wrote:

> Hi,
>
> this is one of the reasons why unsafePerformIO is not type-safe. Lets see
> what's going on by figuring out the types of the various definitions.
>
>
>  cell = unsafePerformIO $ newIORef []
>>
>
> newIORef returns a cell which can hold values of the same type as its
> arguments. The type of the empty list is [a], because an empty list could
> be a list of arbitrary elements. So the overall type of cell is:
>
> cell :: IORef [a]
>
> cell returns a cell which can hold lists of arbitrary elements.
>
>  push i = modifyIORef cell (++ [i])
>>
>
> Lets say i is of some type b. Then cell needs to hold lists of the type b.
> So in this use of cell, the type variable is instantiated to b, and the
> overall type of push is:
>
> push :: b -> IO ()
>
> So push can accept arbitrary values, and appends them to the list hold by
> cell. (Note that ghc reports the type as (a -> IO ()), but that really
> means the same thing as (b -> IO ()).
>
>  main = do
>>  push 3
>>
>
> Here, since you call push with 3, b is chosen to be Int. After this line,
> the cell holds the list [3].
>
>   push "x"
>>
>
> Here, since you call push with "x", b is chosen to be String. After this
> line, the cell holds the list [3, "x"], which is not well-typed. You
> tricked Haskell to produce an ill-typed list by using unsafePerformIO.
>
>   readIORef cell >>= return
>>
>
> Here, it is not clear how you want to instantiate the type variable a in
> the type of cell. So lets assume that we want to return a value of type c,
> and instantiate a with c. So even though at this point, the list contains
> an Int and a String, we can (try to) extract whatever type we want from the
> list. Therefore, the overall type of main is:
>
>  main :: IO [c]
>
>  *Main>  main
>> [(),()]
>>
>
> Now once more, it is not clear how you want to instantiate c, so, by
> default, () is chosen. That default is only active in ghci, by the way.
> main will extract the Int 3 and the String "x" from the list, but treat
> them as if they were of type ().
>
> Here you get lucky: Since there's only one value of type (), ghci can show
> "()" without looking at it too deeply, so even though this program is not
> type-safe in a sense, it works fine in practice. But try forcing ghci to
> consider a more interesting type instead of ():
>
> *Main> main :: IO [Int]
> [3,738467421]
>
> The string "x" is reinterpreted as a number and shown as such. You can try
> other types instead of Int until your ghci crashes because you access some
> memory you shouldn't have looked at or try to execute some random part of
> your memory as code.
>
>
>
> So to summarize, your code exhibits the (well-known) fact that
> unsafePerformIO is not type-safe, because it can be used to implement a
> polymorphic reference, which is a Bad Thing.
>
>  Tillmann
>
>
>
>
>
>
>
>
> ______________________________**_________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/**mailman/listinfo/haskell-cafe<http://www.haskell.org/mailman/listinfo/haskell-cafe>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20120315/f7d7f58b/attachment.htm>


More information about the Haskell-Cafe mailing list