0

I'm having a slight problem implementing a particle based fluid simulation in Haskell for a programming competition. I currently have an array of particles that gets modified at each simulation step. Each particle is a tuple of 2 vectors: position and velocity (my own Vec3D module). At some point I need to extract the positions from the particles (sort of like unzipping lists) which I tried to do like this:

let xs = runSTArray $ do
    xs' <- newArray (min, max) (0.0,0.0,0.0) :: ST s (STArray s Int Vec3D)
    forM_ [min..max] $ \j -> do
        (x, v) <- readArray ps' j
        writeArray xs' j x
    return xs'
let displacements = doubleDensityRelaxation xs restDen k kNear t h

where ps' and doubleDensityRelaxation are of type

type Vec3D = (Double, Double, Double)
ps' :: ST s (STArray s Int (Vec3D, Vec3D))
doubleDensityRelaxation :: Array Int Vec3D -> Double -> Double -> Double -> Double -> Double -> Array Int Vec3D

so xs should be of type xs :: Array Int Vec3D. However, I get

Simulator.hs:76:35:
No instance for (MArray (STArray s) (Vec3D, Vec3D) (ST s1))
  arising from a use of `readArray'
Possible fix:
  add an instance declaration for
  (MArray (STArray s) (Vec3D, Vec3D) (ST s1))
In a stmt of a 'do' block: (x, v) <- readArray ps' j
In the expression:
  do { (x, v) <- readArray ps' j;
       writeArray xs' j x }
In the second argument of `($)', namely
  `\ j
     -> do { (x, v) <- readArray ps' j;
             writeArray xs' j x }'

from the compiler which I dont really understand given that readArray isn't supposed to return an entire array; just one (Vec3D, Vec3D) element.

As a fix, could I make doubleDensityRelaxation take ST s (STArray s Int Vec3D) directly?

If I change the type like that and remove the let xs = runSTArray $ do part I get

Couldn't match expected type `ST s0 (STArray s0 Int Vec3D)'
            with actual type `STArray s Int Vec3D'

but if I give it (ST s xs') as an input instead of just xs' it complains about data constructor ST not being in scope. My imports are currently

import Data.List
import Data.Array
import Data.Array.ST
import Control.Monad
import Control.Monad.ST
import Vec3D  

Complete function:

step :: Array Int (Vec3D, Vec3D) -> Vec3D -> Double -> Double -> Double -> Double -> Double -> Array Int (Vec3D, Vec3D)
step ps g restDen k kNear t h = runSTArray $ do
    ps' <- thaw ps :: ST s (STArray s Int Particle)
    --GRAVITY
    forM_ [min..max] $ \i -> do
        (x, v) <- readArray ps' i
        writeArray ps' i (x, addGravity v g t)
    --TODO - VISCOSITY
    --MOVE
    xsOld <- newArray (min, max) (0.0,0.0,0.0) :: ST s(STArray s Int Vec3D)
    forM_ [min..max] $ \i -> do
        (x, v) <- readArray ps' i
        writeArray xsOld i x
        writeArray ps' i (x `add` (v `mulSc` t), v)
    --TODO - SPRINGS
    --DOUBLE DENSITY RELAXATION
    xs' <- newArray (min, max) (0.0,0.0,0.0) :: ST s (STArray s Int Vec3D)
    forM_ [min..max] $ \j -> do
        (x, v) <- readArray ps' j
        writeArray xs' j x
    let displacements = doubleDensityRelaxation (freeze xs') restDen k kNear t h
    ps <- newArray (min, max) ((0.0,0.0,0.0), (0.0,0.0,0.0)) :: ST s (STArray s Int (Vec3D, Vec3D))
    --TODO incomplete
    return ps
    where
        addGravity v g t = v `add` (g `mulSc` t)
        (min, max) = bounds ps
Cactus
  • 27,075
  • 9
  • 69
  • 149
  • Can you post a complete example? The problem is with the type of `ps'` (it should be `STArray s Int (Vec3D, Vec3D)` without the `ST s ...`), but it's hard to give good advice on how to fix it without seeing where `ps'` is coming from. – hammar Nov 19 '12 at 15:40
  • @hammar - I see your point. If I take the ST s away though in the very beginning, it gives me this: Simulator.hs:60:21: `Couldn't match expected type 'Vec3D' with actual type 'Int'. Expected type: Array Vec3D Vec3D. Actual type: Array Int (Vec3D, Vec3D). In the first argument of 'thaw', namely 'ps'. In a stmt of a 'do' block: ps' <- thaw ps :: STArray s Int Particle` – Sebastian Göb Nov 19 '12 at 16:06
  • 1
    Your updated code compiles if I move the freeze call out of the `let displacements = ... line` to `xs'' <- freeze xs'` and then use `xs''` instead of `freeze xs'` ([hpaste](http://hpaste.org/77920)). – hammar Nov 19 '12 at 16:29
  • @hammar I get `No instance for (Data.Array.Base.IArray b0 Vec3D)` (the type variable `b0` is ambiguous) without type signature, and getting the correct signature is a tad nontrivial. – Daniel Fischer Nov 19 '12 at 16:30
  • 1
    @LemonLord `freeze :: (Ix i, MArray a e m, IArray b e) => a i e -> m (b i e)`. So your `freeze xs'` is an `IArray b Vec3D => ST s (b Int Vec3D)`. To get the actual array, you need to run `freeze` inside the `runSTArray`. – Daniel Fischer Nov 19 '12 at 16:34
  • @hammar: since your comment was the answer, you should post it as an answer. – sclv Feb 01 '13 at 15:59

1 Answers1

0

community-wiki answer based on the comments:

The updated code compiles if you move the freeze call out of the let displacements = ... line to xs'' <- freeze xs' and then use xs'' instead of freeze xs'

This is because freeze :: (Ix i, MArray a e m, IArray b e) => a i e -> m (b i e). So freeze xs' is an IArray b Vec3D => ST s (b Int Vec3D). To get the actual array, you need to run freeze inside the runSTArray.

sclv
  • 38,665
  • 7
  • 99
  • 204