Re: Top Level TWI's again was Re: [Haskell] Re: Parameterized Show

[Switching to Haskell-cafe] At 11:26 22/11/04 +0000, you wrote:
I would ask an alternative question - is it possible to live without unsafePerformIO? I have never needed to use it!
I have used it once, with reservations, but at the time I didn't have the time/energy to find a better solution. (The occasion of its use was accessing external entities within an XML parser; by making the assumption that the external entities do not change within any context in which results from a program are compared, I was able to satisfy the "proof obligation" of not causing or being sensitive to side effects.) The reason this was important to me is that I wanted to be able to use the parser from code that was not visibly in the IO monad. For me, treating Web data transformations as pure functions is one of the attractions of using Haskell. (Since doing that, I had an idea that I might be able to parameterize the entity processing code on some Monad, and use either an Identity monad or IO depending on the actual requirements. This way, I could keep pure XML processing out of the IO monad, but use IO when IO was needed.) In short: I think it's usually possible to avoid using unsafePerformIO, but I'd be reluctant to cede it altogether, if only for sometimes quick-and-dirty pragmatic reasons. #g ------------ Graham Klyne For email: http://www.ninebynine.org/#Contact

Obviously without knowing the details I am speculating, but would it not be possible to do a first pass of the XML and build a list of files to read (a pure function) this returns its result to the IO monad where the files are read and concatenated together, and passed to a second (pure functional) processing function. If written well this can take advantage of lazy execution, so both functions end up running concurrently. It seems to me that as unsafePerformIO is not in the standard and only implemented on some compilers/interpreters, that you limit the portability of code by using it, and that it is best avoided. Also as any safe use of unsafePerformIO can be refactored to not use it I could certainly live without it. Keean. Graham Klyne wrote:
[Switching to Haskell-cafe]
I have used it once, with reservations, but at the time I didn't have the time/energy to find a better solution. (The occasion of its use was accessing external entities within an XML parser; by making the assumption that the external entities do not change within any context in which results from a program are compared, I was able to satisfy the "proof obligation" of not causing or being sensitive to side effects.)
The reason this was important to me is that I wanted to be able to use the parser from code that was not visibly in the IO monad. For me, treating Web data transformations as pure functions is one of the attractions of using Haskell.
(Since doing that, I had an idea that I might be able to parameterize the entity processing code on some Monad, and use either an Identity monad or IO depending on the actual requirements. This way, I could keep pure XML processing out of the IO monad, but use IO when IO was needed.)
In short: I think it's usually possible to avoid using unsafePerformIO, but I'd be reluctant to cede it altogether, if only for sometimes quick-and-dirty pragmatic reasons.
#g
------------ Graham Klyne For email: http://www.ninebynine.org/#Contact

On Monday 22 November 2004 23:22, Keean Schupke wrote:
It seems to me that as unsafePerformIO is not in the standard and only implemented on some compilers/interpreters, that you limit the portability of code by using it, and that it is best avoided. Also as any safe use of unsafePerformIO can be refactored to not use it I could certainly live without it.
With one exception: If a foreign function (e.g. from a C library) is really pure, then I see no way to tell that to the compiler other than using unsafePerformIO. IIRC, unsafePerformIO is in the standard FFI libraries. Ben -- Top level things with identity are evil. -- Lennart Augustsson

Benjamin Franksen writes:
If a foreign function (e.g. from a C library) is really pure, then I see no way to tell that to the compiler other than using unsafePerformIO.
What's the problem with importing it with a pure signature? Like this: foreign import ccall unsafe sin :: CDouble -> CDouble Peter

Can a C function be pure? I guess it can... The trouble is you cannot proove its pure? But - why would you want to use a pure C function. The chances of any useful C library function being pure are slim - and the performance of GHC in some of the benchmarks shows that there is hardly any speed advantage (for a pure function)... Keean. Benjamin Franksen wrote:
On Monday 22 November 2004 23:22, Keean Schupke wrote:
It seems to me that as unsafePerformIO is not in the standard and only implemented on some compilers/interpreters, that you limit the portability of code by using it, and that it is best avoided. Also as any safe use of unsafePerformIO can be refactored to not use it I could certainly live without it.
With one exception: If a foreign function (e.g. from a C library) is really pure, then I see no way to tell that to the compiler other than using unsafePerformIO. IIRC, unsafePerformIO is in the standard FFI libraries.
Ben

Keean Schupke wrote:
Can a C function be pure? I guess it can... The trouble is you cannot proove its pure?
But - why would you want to use a pure C function.
Because it already exists? E.g. most BLAS/LAPACK functions are pure;
should they be re-written in Haskell?
[Yes, I know that BLAS/LAPACK are written in Fortran, but I don't
think that changes the argument. The resulting object code (which is
what you would actually be using) wouldn't be significantly different
if they were written in C.]
--
Glynn Clements

Glynn Clements wrote: I thought these libraries did have some global state, like choosing which solver is used... In which case treating them as pure could be dangerous... Keean.
Keean Schupke wrote:
Can a C function be pure? I guess it can... The trouble is you cannot proove its pure?
But - why would you want to use a pure C function.
Because it already exists? E.g. most BLAS/LAPACK functions are pure; should they be re-written in Haskell?
[Yes, I know that BLAS/LAPACK are written in Fortran, but I don't think that changes the argument. The resulting object code (which is what you would actually be using) wouldn't be significantly different if they were written in C.]

Keean Schupke wrote:
Can a C function be pure? I guess it can... The trouble is you cannot proove its pure?
A C function might have no observable side effects, even if it operates destructively over its own private data structures. It mightn't be too hard to establish a sound test for this sort of purity (the one we have already is sound; it always says no; some improvement may be possible). Clearly completeness is too much to hope for.
But - why would you want to use a pure C function. The chances of any useful C library function being pure are slim - and the performance of GHC in some of the benchmarks shows that there is hardly any speed advantage (for a pure function)...
What about the other benchmarks? There are plenty of operations where programmers can do a neater job than compilers at deciding that a given data structure is known only to one consumer and can therefore be manipulated destructively, recycled aggressively etc. I know modern recycling is marvellous, but reduced consumption is better, isn't it? The C functions I'm thinking of are the output from Hofmann &co's LFPL compiler: pure *linear* functional programs which run in the heap they were born with. There are potential speed gains too: the knowledge that you don't need to keep the original input means that you can operate deep inside it in constant time, at the cost of maintaining some extra pointers. (Does anybody know of a linear type system which allows this? Basically, a list xs contains a pointer to its tail, so holding a tail-pointer for xs would be a duplicate reference: problem. But perhaps it's ok for the holder of xs also to hold its tail-pointer.) This stuff isn't really my thing, but I'm an interested spectator. These programs aren't funny interactive hard-drive-formatting things, so they're probably irrelevant to this particular argument. Nonetheless, they're hard to write efficiently in functional programming languages as we know them. They're hard to write safely in C, but sometimes we just get fed up with knowing useful stuff that we can't tell the compiler. Is uniqueness worth a second look? Conor -- http://www.cs.rhul.ac.uk/~conor for one more week

Have you looked at Linear Aliasing, the type system used for TAL (typed assembly language)... one would assume that if a C compiler which compiles to TAL were produces, then you could proove purity? Keean. Conor McBride wrote:
Keean Schupke wrote:
Can a C function be pure? I guess it can... The trouble is you cannot proove its pure?
A C function might have no observable side effects, even if it operates destructively over its own private data structures. It mightn't be too hard to establish a sound test for this sort of purity (the one we have already is sound; it always says no; some improvement may be possible). Clearly completeness is too much to hope for.
But - why would you want to use a pure C function. The chances of any useful C library function being pure are slim - and the performance of
GHC in some of the benchmarks shows that there is hardly any speed advantage (for a pure function)...
What about the other benchmarks? There are plenty of operations where programmers can do a neater job than compilers at deciding that a given data structure is known only to one consumer and can therefore be manipulated destructively, recycled aggressively etc. I know modern recycling is marvellous, but reduced consumption is better, isn't it?
The C functions I'm thinking of are the output from Hofmann &co's LFPL compiler: pure *linear* functional programs which run in the heap they were born with. There are potential speed gains too: the knowledge that you don't need to keep the original input means that you can operate deep inside it in constant time, at the cost of maintaining some extra pointers. (Does anybody know of a linear type system which allows this? Basically, a list xs contains a pointer to its tail, so holding a tail-pointer for xs would be a duplicate reference: problem. But perhaps it's ok for the holder of xs also to hold its tail-pointer.) This stuff isn't really my thing, but I'm an interested spectator.
These programs aren't funny interactive hard-drive-formatting things, so they're probably irrelevant to this particular argument. Nonetheless, they're hard to write efficiently in functional programming languages as we know them. They're hard to write safely in C, but sometimes we just get fed up with knowing useful stuff that we can't tell the compiler.
Is uniqueness worth a second look?
Conor
-- http://www.cs.rhul.ac.uk/~conor for one more week

On Tuesday 23 November 2004 10:03, you wrote:
But - why would you want to use a pure C function. The chances of any useful C library function being pure are slim - and the performance of GHC in some of the benchmarks shows that there is hardly any speed advantage
The typical case (for me) is a foreign library exporting mostly non-pure routines, but with one or two pure functions among them. But as has been stated already, unsafePerformIO is not needed in this case. BTW, if you reply to the list anyway, don't reply to me in person. Otherwise I get everything duplicated (and it goes onto the wrong folder ;-). Ben -- Top level things with identity are evil. -- Lennart Augustsson

I think this is a useful debate, because it touches on how Haskell meets real-world programming needs, so I shall continue in that spirit... At 22:22 22/11/04 +0000, Keean Schupke wrote:
Obviously without knowing the details I am speculating, but would it not be possible to do a first pass of the XML and build a list of files to read (a pure function) this returns its result to the IO monad where the files are read and concatenated together, and passed to a second (pure functional) processing function. If written well this can take advantage of lazy execution, so both functions end up running concurrently.
In an ideal world, it is certainly possible to separate the pure and non-pure aspects of the code, and do something like you suggest. But my position was that I was working with an existing codebase (HaXml) which had not been structured with this requirement in mind, and I absolutely did not want to start from scratch (as it was, I was forced into some substantial refactoring). This was one case where, in order to get any result at all with the time/effort available to me, I needed to hide the I/OI within an otherwise pure function. Yes, there are better ways but, being a "Bear of Very Little Brain", I have to work with the tools, intellectual and otherwise, that are at my disposal. Most software is not built in the optimum fashion, or even anything close to it. I would suggest that one of the challenges for functional programming is to maker it easy to "do the right thing". I came to functional programming with quite a strong bias to make it work for me, inspired many years ago by John Backus' famous paper, and a presentation by David Turner about KRC, and a few other things. Many programmers I've spoken to who have tried functional programing have given up on it because it's too hard.
It seems to me that as unsafePerformIO is not in the standard and only implemented on some compilers/interpreters, that you limit the portability of code by using it, and that it is best avoided. Also as any safe use of unsafePerformIO can be refactored to not use it I could certainly live without it.
Well, I am concerned about portability. I insist on using Hugs when many people use just GHC, and one of the reasons is that I don't want to get locked into one compiler's extensions. But sometimes it is necessary to use extensions: there are many features of Haskell-98++ that are almost essential (IMO) to practical software development. Including, I think, unsafePerformIO (on rare occasions). My touchstone is that I'll use language extensions when I have to, provided they are supported by both Hugs and GHC. What's my point in all this? I supposed it might be summed up as: "The best is the enemy of the good". #g --
Graham Klyne wrote:
[Switching to Haskell-cafe]
I have used it once, with reservations, but at the time I didn't have the time/energy to find a better solution. (The occasion of its use was accessing external entities within an XML parser; by making the assumption that the external entities do not change within any context in which results from a program are compared, I was able to satisfy the "proof obligation" of not causing or being sensitive to side effects.)
The reason this was important to me is that I wanted to be able to use the parser from code that was not visibly in the IO monad. For me, treating Web data transformations as pure functions is one of the attractions of using Haskell.
(Since doing that, I had an idea that I might be able to parameterize the entity processing code on some Monad, and use either an Identity monad or IO depending on the actual requirements. This way, I could keep pure XML processing out of the IO monad, but use IO when IO was needed.)
In short: I think it's usually possible to avoid using unsafePerformIO, but I'd be reluctant to cede it altogether, if only for sometimes quick-and-dirty pragmatic reasons.
#g
------------ Graham Klyne For email: http://www.ninebynine.org/#Contact
------------ Graham Klyne For email: http://www.ninebynine.org/#Contact

Off topic, but interesting, Someone else keeps quoting this at me... I prefer Knuth - paraphrased as I cant remember the quote - The best software projects are the ones where the source code has been lost about half way through the development and started from scratch. The point is programmers start by exploring a problem space without understanding it. Poor programmers just accept the first solution they put down. Good programmers re-implement. Great programmers have a sixth sense of when things are about to get ugly, and start again (and the better you are the less you actually have to implement before you realise things can be refactored for the better)... Graham Klyne wrote:
What's my point in all this? I supposed it might be summed up as: "The best is the enemy of the good".
#g --

At 10:02 23/11/04 +0000, you wrote:
Off topic, but interesting,
Sure... that's why its in 'cafe, right?
Someone else keeps quoting this at me... I prefer Knuth - paraphrased as I cant remember the quote - The best software projects are the ones where the source code has been lost about half way through the development and started from scratch.
The point is programmers start by exploring a problem space without understanding it. Poor programmers just accept the first solution they put down. Good programmers re-implement. Great programmers have a sixth sense of when things are about to get ugly, and start again (and the better you are the less you actually have to implement before you realise things can be refactored for the better)...
Graham Klyne wrote:
What's my point in all this? I supposed it might be summed up as: "The best is the enemy of the good".
Hmmm... I take your point, and I think my attempted pithy summary missed its intended target. What I was trying to convey was a sense that a great language has to let merely average (or worse) programmers do a halfway decent job. There aren't enough great programmers to go round. And even great programmers sometimes have to work with someone else's codebase (which even if written by a great programmer may have had diffent goals in mind). (FWIW, I think Python is a language that scores pretty highly on this count.) #g ------------ Graham Klyne For email: http://www.ninebynine.org/#Contact

On Mon, Nov 22, 2004 at 08:32:33PM +0000, Graham Klyne wrote:
[Switching to Haskell-cafe]
At 11:26 22/11/04 +0000, you wrote:
I would ask an alternative question - is it possible to live without unsafePerformIO? I have never needed to use it!
There are plenty of non-IO reasons to use unsafePerformIO, for which it is essential. If you want to write haskell code that uses a pointer (allocated possibly via an FFI C routine), it has to be in the IO monad. If you know that this pointer doesn't access memory that'll be changed at random (or by other routines), you can (and *should*) safely use unsafePerformIO. Also, if you're interested in using weak pointers (for example, to do memoization), you'll almost certainly need to use unsafePerformIO. Again, the result can, and should, be encapsulated, so the module that uses unsafePerformIO exports only pure functions (unless of course, there are any that actually perform IO). -- David Roundy http://www.darcs.net

David Roundy wrote:
There are plenty of non-IO reasons to use unsafePerformIO, for which it is essential. If you want to write haskell code that uses a pointer (allocated possibly via an FFI C routine), it has to be in the IO monad. If you know that this pointer doesn't access memory that'll be changed at random (or by other routines), you can (and *should*) safely use unsafePerformIO.
Does it? cant you just declare: import foreign ccall "somefn" somefn :: Ptr Double -> Ptr Double
Also, if you're interested in using weak pointers (for example, to do memoization), you'll almost certainly need to use unsafePerformIO. Again, the result can, and should, be encapsulated, so the module that uses unsafePerformIO exports only pure functions (unless of course, there are any that actually perform IO).
Don't know about this one, got a short example? Keean.

On Tue, Nov 23, 2004 at 01:51:24PM +0000, Keean Schupke wrote:
David Roundy wrote:
There are plenty of non-IO reasons to use unsafePerformIO, for which it is essential. If you want to write haskell code that uses a pointer (allocated possibly via an FFI C routine), it has to be in the IO monad. If you know that this pointer doesn't access memory that'll be changed at random (or by other routines), you can (and *should*) safely use unsafePerformIO.
Does it? cant you just declare:
import foreign ccall "somefn" somefn :: Ptr Double -> Ptr Double
Right, but if you want to access the contents of that pointer in haskell, you have to use the IO monad. True, in principle you could write a pointer dereferencing function in C: import foreign ccall "readarray" readarray :: Ptr Double -> Int -> Double but that hardly seems like either an efficient or elegant way of getting around the fact that haskell can't read pointers outside the IO monad. Also, of course, this readarray function written in C is no safer than using unsafePerformIO with peekArray. In case you're wondering, peekArray needs to be in the IO monad because there's no guarantee that the memory pointed to by the Ptr is constant--it may even be a pointer to an mmapped file, in which case it could change value independently of the program's execution.
Also, if you're interested in using weak pointers (for example, to do memoization), you'll almost certainly need to use unsafePerformIO. Again, the result can, and should, be encapsulated, so the module that uses unsafePerformIO exports only pure functions (unless of course, there are any that actually perform IO).
Don't know about this one, got a short example?
I have a long and complicated example... http://abridgegame.org/cgi-bin/darcs.cgi/darcs/AntiMemo.lhs?c=annotate This is complicated because it's doing antimemoization rather than memoization, and being backwards it's a bit trickier. But it *is* an example of a module that exports only pure functions, and couldn't be written without unsafePerformIO (and does no IO). -- David Roundy http://www.darcs.net
participants (8)
-
Benjamin Franksen
-
Conor McBride
-
David Roundy
-
Glynn Clements
-
Graham Klyne
-
Graham Klyne
-
Keean Schupke
-
Peter Simons