* DapperDan2: it strikes me that ST is like a lexical scope, where all the variables/state disappear when the function returns.

* DapperDan2: it strikes me that ST is like a lexical scope, where all the variables/state disappear when the function returns.

Line 38:

Line 36:

</haskell>

</haskell>

−

The action you pass must be universal in s, so inside your action you

+

The action you pass must be universal in s, so inside your action you don't know what thread, thus you cannot access any other threads, thus <hask>runST</hask> is pure. This is very useful, since it allows you to implement externally pure things like in-place quicksort, and present them as pure functions ∀ e. Ord e ⇒ Array e → Array e; without using any unsafe functions.

−

don't know what thread, thus you cannot access any other threads, thus

+

−

<hask>runST</hask> is pure. This is very useful, since it allows you to implement

+

−

externally pure things like in-place quicksort, and present them as pure

+

−

functions ∀ e. Ord e ⇒ Array e → Array e; without using any unsafe

+

−

functions.

+

−

But that type of <hask>runST</hask> is illegal in Haskell-98, because it needs a

+

But that type of <hask>runST</hask> is illegal in Haskell-98, because it needs a universal quantifier *inside* the function-arrow! In the jargon, that type has rank 2; haskell 98 types may have rank at most 1.

−

universal quantifier *inside* the function-arrow! In the jargon, that

+

−

type has rank 2; haskell 98 types may have rank at most 1.

+

See http://www.haskell.org/pipermail/haskell-cafe/2007-July/028233.html

See http://www.haskell.org/pipermail/haskell-cafe/2007-July/028233.html

−

Could we *please* see an example.

+

== A few simple examples ==

−

+

−

== A simple example ==

+

In this example, we define a version of the function sum, but do it in a way which more like how it would be done in imperative languages, where a variable is updated, rather than a new value is formed and passed to the next iteration of the function. While in place modifications of the STRef n are occurring, something that would usually be considered a side effect, it is all done in a safe way which is deterministic. The result is that we get the benefits of being able to modify memory in place, while still producing a pure function with the use of runST.

In this example, we define a version of the function sum, but do it in a way which more like how it would be done in imperative languages, where a variable is updated, rather than a new value is formed and passed to the next iteration of the function. While in place modifications of the STRef n are occurring, something that would usually be considered a side effect, it is all done in a safe way which is deterministic. The result is that we get the benefits of being able to modify memory in place, while still producing a pure function with the use of runST.

Line 90:

Line 79:

readSTRef acc' -- and finally read the result

readSTRef acc' -- and finally read the result

</haskell>

</haskell>

+

+

An example of the Fibonacci function running in constant¹ space:

+

+

<haskell>

+

fibST :: Integer -> Integer

+

fibST n =

+

if n < 2

+

then n

+

else runST $ do

+

x <- newSTRef 0

+

y <- newSTRef 1

+

fibST' n x y

+

+

where fibST' 0 x _ = readSTRef x

+

fibST' n x y = do

+

x' <- readSTRef x

+

y' <- readSTRef y

+

writeSTRef x y'

+

writeSTRef y $! x'+y'

+

fibST' (n-1) x y

+

</haskell>

+

+

[1] Since we're using Integers, technically it's not constant space, as they grow in size when they get bigger, but we can ignore this.

Contents

TuringTest: ST lets you implement algorithms that are much more efficient with mutable memory used internally. But the whole "thread" of computation cannot exchange mutable state with the outside world, it can only exchange immutable state.

TuringTest: chessguy: You pass in normal Haskell values and then use ST to allocate mutable memory, then you initialize and play with it, then you put it away and return a normal Haskell value.

sjanssen: a monad that has mutable references and arrays, but has a "run" function that is referentially transparent

DapperDan2: it strikes me that ST is like a lexical scope, where all the variables/state disappear when the function returns.

The ST monad lets you use update-in-place, but is escapable (unlike IO).
ST actions have the form:

ST s α

Meaning that they return a value of type α, and execute in "thread" s.
All reference types are tagged with the thread, so that actions can only
affect references in their own "thread".

Now, the type of the function used to escape ST is:

runST ::forall α.(forall s. ST s α)-> α

The action you pass must be universal in s, so inside your action you don't know what thread, thus you cannot access any other threads, thus

runST

is pure. This is very useful, since it allows you to implement externally pure things like in-place quicksort, and present them as pure functions ∀ e. Ord e ⇒ Array e → Array e; without using any unsafe functions.
But that type of

runST

is illegal in Haskell-98, because it needs a universal quantifier *inside* the function-arrow! In the jargon, that type has rank 2; haskell 98 types may have rank at most 1.

In this example, we define a version of the function sum, but do it in a way which more like how it would be done in imperative languages, where a variable is updated, rather than a new value is formed and passed to the next iteration of the function. While in place modifications of the STRef n are occurring, something that would usually be considered a side effect, it is all done in a safe way which is deterministic. The result is that we get the benefits of being able to modify memory in place, while still producing a pure function with the use of runST.