When does GHC internally mutate immutable values?

I want to use Haskell for a real-time application that consists of an ever-changing dire condition.

The state is immutable, of course, so at each step of the state, I recreate a new slightly altered state and discard the old one. In this case, it will be painfully ineffective because I don't need the previous states.

I have often met people who say that GHC can optimize things like this and mutate unchanging values ​​internally, and I want to make sure that it does.

Is it possible? is there a way to determine if GHC will optimize it by internally changing the value? is there a way to secure it / make sure it will?

PS Is there a formal name for this optimization?

+3


source to share


3 answers


The GHC itself does not. The various container libraries use a trick called streaming fusion, which means some of the copies that purely functional code offers were never actually created - but this is still not a true "internal mutation", but rather a grouping of multiple operations, each one will include the copy in one large operation with another copy.

I don't think it is really possible to get true "mutation-optimization" in a fully automatic way; some languages, like Mercury , they claim to do it, but I really don't know how well it works.



However, a good pure functional language like Haskell is quite capable of working with mutable state explicitly: via monads. It can be either an "omnipotent" monad IO

(somewhat frowning because you lose all ref-transp guarantees. But for a real-time application, this is probably correct) or a specialized ST

monad
whose purpose is to allow you to use true mutable state while keeping while the external behavior of the program is purely functional.
If you take this approach, you not only make sure there are no costly copies, but you will probably get better code. Because sometimes mutation is just the right way to think about a problem; even code that is really purely functional sometimes gets better if you "pretend" to use mutable state in State

monad
.

+9


source


AFAIK ghc

doesn't do this kind of optimization in the general case. This probably requires unique types .

But this runtime is optimized for such a "slightly changed state" case. Usually your state is (or can be thought of as) something like a tree, and most manjas actually reuse most of the existing tree. This way the modification is driven by only a few pointers and is very efficient. Let's consider an example:



data State = State
  { theA :: A
  , theB :: B
  }

data A = A Int
data B = B String

modifyTheA :: (A -> A) -> State -> State
modifyTheA f s = s {theA = f (theA s)}

      

Here the modifyTheA

function creates a new one State

, but it's just two pointers. The whole line theB

is reused.

+4


source


GHC performs this optimization under one condition: if the object is used once in the function where it is declared, and no additional references are made to it. It does not apply if the object is created and used in separate functions unless the source function is enabled.

There is another related optimization that GHC does more reliably. If the last action of the function is to call itself with basically the same arguments, then it won't even touch the arguments that don't change.

+2


source







All Articles