21

In the concurrency library GHC.Conc there is a function called numCapabilities. Its type is numCapabilities :: Int and it actually returns some number you passed by the command line flag (e.g. 5 if the options are +RTS -N 5).

However, getArgs (type: IO [String]) does essentially the same (it returns the unparsed non-runtime arguments) but isn't a pure function.

If the only excuse is that numCapabilities is often needed in pure code, in what way aren't other command line options not needed in pure code?

Am I something missing or is either numCapabilities a design flaw or am I allowed to write the following monster?

myGetArgs = unsafePerformIO getArgs
helami
  • 2,099
  • 2
  • 14
  • 17
  • 6
    I'm now wondering the opposite: Since the value of `getArgs` never changes during the run of your program, I'm not exactly sure why it needs to/should be in `IO`. – sepp2k Aug 07 '12 at 17:31
  • 4
    As mentioned in one of the answers, it depends on how one defines a _pure_ expression. I'd define an expression to be _pure_ iff it doesn't depend on anything but the expression itself. By this definition `numCapabilities` isn't pure, so having a type of `Int` should be considered as a design flaw. You may be interested in Conal Elliott's blog post [Notions of purity in Haskell](http://conal.net/blog/posts/notions-of-purity-in-haskell). – Petr Aug 07 '12 at 18:05
  • `numCapabilities` need not return the value given to `-N` - it just calls `getNumCapabilities`, so returns however many capabilities there are at the time. I think of this as a bug, really, but it's hard to say what guarantees you can rely on in the GHC modules. – Ben Millwood Aug 18 '12 at 21:56
  • (Notice that Control.Concurrent exports `getNumCapabilities` but not `numCapabilities`. If you stick to the "standard" interface, you don't see the impurity.) – Ben Millwood Aug 18 '12 at 21:57

4 Answers4

22

I've seen very varying views on what to do in situations like this. Some think that values that might vary between compiles should not be pure, and some think that as long as a value doesn't change during your program's local run-time (i.e. after some "configuration" has been "set up" in main), it should be pure.

The base package seems to have settled on a middle-ground. numCapabilities will not (as far as I know) change during run-time, but getArgs might.

This is because there is a withArgs function that changes the args you get via getArgs. So, that answers that.

dflemstr
  • 25,947
  • 5
  • 70
  • 105
  • 1
    What about [`setNumCapabilities`](http://www.haskell.org/ghc/docs/latest/html/libraries/base/GHC-Conc.html#v:setNumCapabilities)? – Ptharien's Flame Aug 07 '12 at 18:03
  • 9
    `numCapabilities` returns the parameter to `-N#`, while `getNumCapabilities` returns how many capabilities there actually are. `setNumCapabilities` changes the actual number, which is retrieved by `getNumCapabilities`. Both of the `get*` and `set*` functions are in the `IO` monad. – dflemstr Aug 07 '12 at 18:05
  • Which leads to the question why there are both `numCapabilities` and `getNumCapabilities` and not both `args` and `getArgs` – helami Aug 07 '12 at 18:06
  • 12
    Because `setNumCapabilities` was added after `numCapabilities`, and `numCapabilities` is deprecated (as in, the library author says that you should use the `get*` function in the documentation for `numCapabilities`). – dflemstr Aug 07 '12 at 18:08
  • 1
    `numCapabilities` doesn't actually return the parameter to `-N#`, cf. my answer. This means that the answer is (partially) incorrect too. – Ben Millwood Apr 07 '13 at 01:59
  • 2
    It's a deprecated value. It definitely used to return the parameter passed to `-N#` before, since it was impossible to change that number (because there was no `setNumCapabilities`). After the new functions were introduced, `numCapabilities` was deprecated, and is only kept for compatibility, so it does still behave consistently for programs that are not aware of `set*`. You should not use it for new programs; in other words, a program containing both `numCapabilities` and `setNumCapabilities` is semantically incorrect. – dflemstr Apr 07 '13 at 07:47
15

Oh dear. If you look at the definition of numCapabilities, you can see it is just:

numCapabilities :: Int
numCapabilities = unsafePerformIO $ getNumCapabilities

and the following ghci sessions illustrate the problem:

[ben@euler ~]$ ghci
GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
Loading [...]
ghci> :m +GHC.Conc
ghci> numCapabilities
1
ghci> setNumCapabilities 2
ghci> numCapabilities
1
ghci> :q
Leaving GHCi.

[ben@euler ~]$ ghci
GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
Loading [...]
ghci> :m +GHC.Conc
ghci> setNumCapabilities 2
ghci> numCapabilities
2

This is definitely bad - the value of numCapabilities depends on when it is evaluated with respect to any setNumCapabilities calls that may exist in your program. Note that in the first session, numCapabilities remained consistent, since the IO is only executed the first time it is evaluated. However, in the presence of inlining (the name isn't marked NOINLINE or anything) even that may not be true - you could in principle get two different values from two occurrences of numCapabilities (although in practice I haven't been able to make this happen).

So the answer is that numCapabilities isn't a pure function, but is erroneously marked as such by the notorious back door of unsafePerformIO.

Ben Millwood
  • 6,754
  • 24
  • 45
3

I'd say it's a mistake, but it depends on what one thinks purity is. See the post Notions of purity in Haskell and its discussion. Simply put, the post's argument is that types have meaning, and there is not room in the meaning of Int for something like numCapabilities, which is execution-context-dependent.

Conal
  • 18,517
  • 2
  • 37
  • 40
0

numCapabilities gives the initial value of getNumCapabilities, wether or not the RTS -N flag argument is present, so the type should be the same.

Have you tried it with a higher number than your computer max. simultaneous threads ?

$ ghci +RTS -N99
GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
Prelude> :m +GHC.Conc
Prelude GHC.Conc> numCapabilities
99
Prelude GHC.Conc> getNumCapabilities
99   !!!
Gabriel Riba
  • 6,698
  • 2
  • 17
  • 19