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

2012-03-15 Thread gladstein
Why does the following program compile and produce the results it does?
It seems like 3 and x got interpreted as the same type, (). Thanks in
advance for your help.
 
import Data.IORef
import System.IO.Unsafe

cell = unsafePerformIO $ newIORef []

push i = modifyIORef cell (++ [i])

main = do
 push 3
 push x
 readIORef cell = return

*Main :browse
cell :: GHC.IORef.IORef [a]
push :: a - IO ()
main :: IO [a]

*Main main
[(),()]



___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


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

2012-03-15 Thread Никитин Лев
Maybe everytime you use 'cell' you tell haskell to create NEW cell.

Try this:

push' i cell = modifyIORef cell (++ [i])

main = do
   cell - newIORef []
   push' x cell {- push' 3 cell will be incorrect in this case -}
   push' o cell
   readIORef cell = return


Why the original code porduces [(),()]  but not [] I cannot undestand.

15.03.2012, 23:53, gladst...@gladstein.com gladst...@gladstein.com:
 Why does the following program compile and produce the results it does?
 It seems like 3 and x got interpreted as the same type, (). Thanks in
 advance for your help.

 import Data.IORef
 import System.IO.Unsafe

 cell = unsafePerformIO $ newIORef []

 push i = modifyIORef cell (++ [i])

 main = do
  push 3
  push x
  readIORef cell = return

 *Main :browse
 cell :: GHC.IORef.IORef [a]
 push :: a - IO ()
 main :: IO [a]

 *Main main
 [(),()]

 ___
 Haskell-Cafe mailing list
 Haskell-Cafe@haskell.org
 http://www.haskell.org/mailman/listinfo/haskell-cafe

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


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

2012-03-15 Thread Tillmann Rendel

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@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


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

2012-03-15 Thread Jake McArthur
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 ren...@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@haskell.org
 http://www.haskell.org/**mailman/listinfo/haskell-cafehttp://www.haskell.org/mailman/listinfo/haskell-cafe

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe