What is an unsafe function in Haskell?
Asked Answered
B

2

11

I believe that an unsafe function is a function that says that it will return a value of some type, but that it can actually throw an exception and end the execution therefore not returning any value at all, but I'm not sure.

Or could it be that an unsafe function is a function that can return a value of other type than the declared in the signature? Wouldn't that be a weakly typed function?

Or are weakly typed and unsafe synonyms in Haskell?

This may be a silly question, but I couldn't find a straightforward answer.

I checked the documentation of readLn hoping to see a reference to unsafe functions, but there wasn't any.

This article, Unsafe functions, says something about breaking the type system, but it doesn't specify in which manner; through exceptions?, through returning values of the wrong type?

So, what is an unsafe function in Haskell?

Bilk answered 27/9, 2015 at 2:40 Comment(3)
"Unsafe" has no formal definition. I believe it's used as "can break the normal rules of the language".Propst
Looking at that wiki article, calling seq unsafe is a bit of an extreme position.Salvatore
@melpomene, nowadays, "unsafe" is a Humpty Dumpty word that means "can break the normal rules of" the language the speaker thinks they wish they were programming in, yet for some reason are not. It used to mean dfeuer's 1-4 (definitely not 5 or 6).Subjoin
T
22

There are several notions of "unsafe" in Haskell.

  1. Calculating the value leads to I/O. The prime suspect here is unsafePerformIO. It's a bit controversial whether lazy I/O and unsafeInterleaveIO should be considered unsafe by this definition.

  2. Something breaks the type system. The prime suspect is unsafeCoerce, but unsafePerformIO can do it too.

  3. Something breaks memory safety without breaking the type system (thanks to Carl for reminding me). The prime suspects are unsafe array or vector indexing operations and incorrect use of the foreign function interface.

  4. The result of the calculation depends on the order of evaluation. The prime suspect is unsafePerformIO but unsafeInterleaveST can certainly do it too.

  5. Evaluation can lead to an exception or infinite loop. This is a relatively mild sort of unsafety ... except when it's not.

  6. Something breaks the conventions ("laws") Haskell programmers rely on to reason about their code. Whether this should be considered "unsafe" is subject to debate. Examples: applying seq to a function, using coerce in such a manner as to change the arity of a function relative to its reference implementation and cause a problem if someone applies seq to what was previously a partial application and now can possibly be bottom (there are good performance reasons to do this in some cases), writing class instances that break functor, applicative, monad, traversable, etc., laws. Expecting arguments to satisfy pre-conditions but not checking that they do (e.g., functions that quickly turn ascending lists into sets or maps).

Safe Haskell

To help programmers control some of these forms of unsafety, the Safe Haskell system classifies modules as safe or unsafe depending on the imports and language extensions they use. I haven't studied the details, but GarethR indicates that

I think your notion 1 through 3 would be considered unsafe by Safe Haskell. It may be worth reading up on it because the Safe Haskell authors have clearly thought deeply on safety.

and Ørjan Johansen indicates that

Safe Haskell also disallows some things that fit in point 6, such as extensions that can circumvent module export boundaries (Template Haskell, generalized newtype deriving) or change the behavior of imported code (rules, overlapping instances).

A programmer can mark a module Safe to indicate that they want GHC to check that it is safe, Unsafe to indicate that it is not safe, or Trustworthy to indicate that the author claims to believe that its API is safe to use despite the fact that its implementation uses unsafe features.

Tiffaneytiffani answered 27/9, 2015 at 3:9 Comment(8)
coerce can affect function arity? I'd have expected arity analysis to see through newtypes...Salvatore
@OrjanJohansen, I guess I didn't describe that quite right. It can change the arity of a function using it compared to the reference implementation. fmap f (Identity x) = Identity (f x) gives fmap arity 2. fmap = coerce gives it arity 1. So someone relying on that having arity 2 (by using shady seq) will get surprised.Tiffaneytiffani
You missed cases that sacrifice memory safety, like those that show up in Vector or Array.Toler
Excellent answer. You forgot to add the notion of Safe Haskell. I think your notion 1 through 3 would be considered unsafe by Safe Haskell. It may be worth reading up on it because the Safe Haskell authors have clearly thought deeply on safety.Hagbut
@GarethR, I'll add it, but that is something of am inverse, because it is conservative.Tiffaneytiffani
Safe Haskell also disallows some things that fit in point 6, such as extensions that can circumvent module export boundaries (Template Haskell, generalized newtype deriving) or change the behavior of imported code (rules, overlapping instances).Salvatore
I believe that recent versions of GHC will not be affected by the 'arity' issue because of Call Arity.Yodel
@user2407038, that's a different issue altogether. The Call Arity analysis is about finding situations where the compiler can raise its estimation of the arity of a function without changing meaning, to enable certain optimizations that mostly affect folds. Here the code and meaning are different. Try each of the fmap implementations I described with fmap undefined `seq` 4. Note that base-4.8 uses the coerce-based implementation, so if you want to play with this in different versions you should write your own copy of Identity.Tiffaneytiffani
S
2

from haskell FFI wiki:

If you annotate a foreign import declaration with the unsafe keyword, this indicates to the compiler that (1) the call will not invoke another Haskell function, directly or indirectly, and (2) you don't mind if any other running Haskell threads in the system are blocked for the duration of the call.

Sextain answered 4/6, 2018 at 9:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.