Is Haskell really a purely functional language, considering unsafePerformIO?

Haskell is usually referred to as an example of a purely functional language. How can this be justified, given the existence of System.IO.Unsafe.unsafePerformIO ?

Edit: I thought that with "purely functional" it was understood that it was impossible to enter unclean code into the functional part of the program.

+44
type-systems functional-programming haskell referential-transparency unsafe-perform-io
Jun 26 '10 at 16:25
source share
7 answers

Languages ​​We Call Haskell

unsafePerformIO is part of the external function interface specification, not the basic Haskell 98 specification . It can be used for local side effects that do not go beyond a certain area to reveal a purely functional interface. That is, we use it to hide effects when the type checker cannot do this for us (unlike the ST monad, which hides effects with a static guarantee).

To illustrate just a few languages ​​that we call "Haskell", consider the image below. Each ring corresponds to a specific set of computational functions, sorted by security, and with an area that correlates with expressive power (i.e., the number of programs that you can write if you have this function).

A language known as Haskell 98 is listed at the bottom right, allowing for general and partial functions. Agda (or Epigram ), where only full functions are allowed, are even less expressive, but "cleaner" and more secure. Although Haskell, as we use it today, includes everything in FFI where the unsafe PerformIO lives. That is, you can write something in modern Haskell, although if you use things from the outer rings, it will be more difficult to establish the safety and security guarantees made by simple inner rings.

alt text

Thus, Haskell programs are usually not built from 100% link-transparent code, however this is the only moderately common language that is clean by default.

+115
Jun 26 '10 at 17:04 on
source share

I thought with "purely functional" it was implied that it was impossible to enter unclean code ...

The real answer is that unsafePerformIO not part of Haskell, moreover, the garbage collector or runtime system is part of Haskell. unsafePerformIO exists in the system, so the people who build the system can create a pure functional abstraction over very efficient equipment. All real languages ​​have loopholes that allow system developers to do something more efficient than abandoning C code or assembly code.

As for the broader picture of how side effects and I / O are embedded in Haskell through the IO monad, I think the easiest way to think about Haskell is that it is a pure language that describes efficient computing. When the calculation described is main , the runtime system accurately performs these effects.

unsafePerformIO - a way to get effects in an unsafe way; where "unsafe" means "security must be guaranteed by the programmer" - nothing is checked by the compiler. If you are an experienced programmer and are ready to fulfill the obligations associated with serious evidence, you can use unsafePerformIO . But at this point you are no longer programming in Haskell; You are programming in an unsafe language that is very similar to Haskell.

+34
Jun 26 '10 at 17:20
source share

The language / implementation is purely functional. It includes a pair of "escape hatches" that you do not need to use if you do not want to.

+4
Jun 26 '10 at 16:28
source share

I don't think unsafePerformIO means that haskell is somehow becoming unclean. You can create pure (referentially transparent) functions from impure functions.

Consider a skipist. For it to work well, it requires access to the RNG, an unclean function, but this does not make the data structure unclean. If you add an item and then convert it to a list, the same list will be returned every time you add an item.

For this reason, I think unsafePerformIO should be considered promPureIO. A function, which means that functions that have side effects and therefore will be marked as impure by the type system, can be recognized as referentially transparent by the type system.

I understand that for this you need to have a slightly weaker definition of the pure. pure functions are referentially transparent and are never called because of a side effect (such as printing).

+2
Jan 04 2018-11-11T00:
source share

Unfortunately, the language has to do some work in the real world, and this involves communicating with the external environment.

It’s good that you can (and should) limit the use of this "out of style" code to a few specific well-documented parts of your program.

+1
Jun 26 '10 at 16:29
source share

Safe Haskell, the latest GHC extension, provides a new answer to this question. unsafePerformIO is part of the GHC Haskell, but is not part of the safe dialect.

unsafePerformIO should only be used to create link-transparent functions; e.g. memoization. In these cases, the author of the package marks it as "trustworthy." A safe module can only import safe and reliable modules; it cannot import unsafe modules.

For more information: GHC Handbook , Haskell Safety Paper

+1
Jul 24 '12 at 12:11
source share

I have a feeling that I will be very unpopular to say what I am going to say, but I felt that I have to answer some of the (in my opinion, incorrect) information presented here.

Although it is true that unsafePerformIO was officially added to the language as a complement to FFI, the reasons for this are mostly historical rather than logical. It existed informally and was widely used long before Haskell ever had FFI. This was never officially part of the core Haskell standard because, as you noticed, it was just too embarrassing. Probably the hope was that at some point in the future it would simply disappear. Well, this did not happen, and will not, in my opinion.

The development of the FFI add-on served as a convenient pretext for the unsafe PerformanceIO to gain access to the official language standard, since, apparently, this is not too bad compared to the addition of the ability to call external (IE C) code (where all bets are disabled relative to static cleanliness and type safety anyway). It was also convenient to add it here for political reasons. This has fostered the myth that Haskell would be clean if it weren’t for all the dirty "poorly designed" C or "poorly designed" operating systems, or the "poorly designed" hardware or ... whatever. unsafePerformIO is regularly used with FFI-related code, but the reasons for this are often due to poor FFI design and even to Haskell itself, a good design for any foreign object that Haskell is also trying to use the interface.

So, according to Norman Ramsey, the official position was that it was normal to use unsafePerformIO if certain evidence was satisfied by those who used it (first of all, it invalidates important compiler transformations, such as inline and general subtitles, exception expression) . So far, so good, or so you might think. The real kicker is that these evidential obligations cannot be satisfied , which is probably the most common use of the unsafe PerformIO, which in my opinion makes up more than 50% of all unsafe PerformIOs in the wild, I'm talking about the terrifying idiom known as an “unsafe PerformanceIO hack,” which is provably (actually explicitly) completely unsafe (with inlining and cse).

Actually, I don’t have the time, space or inclination to understand what an “unsafe PerformIO hack is” or why it is needed in real IO libraries, but the point is that people who work with the IO Haskells infrastructure are usually “stuck” between a stone and a solid place. " They can either provide an inherent security API that does not have a secure implementation (in Haskell), or they can provide an inherently insecure API that can be implemented safely, but what they rarely can do is provide security in both the design of the API and and . Judging by the depressing regularity with which the "unsafe PerformIO hack" appears in real-world code (including the Haskell standard libraries), it seems that most choose the previous option as the lesser of two evils and just hope that the compiler will not guess things with inlays, cse or any other conversion.

I would like all this to be wrong. Unfortunately it is so.

0
Jun 27 2018-10-06T00:
source share



All Articles