Question

So I'm using Data.VectorSpace and I'm trying to extend on force-layout.

Now I want to produce a polymorphic '1'-scalar. That is, a scalar that, if it were multiplied with a vector, would produce the same vector, regardless of the type (parameter) of that vector. Is it possible? Is there a useful workaround?

Here's a more concrete code example (it continues from the code I've worked with here):

data Particle v = Particle { _pos   :: Point v
                           , _vel   :: v
                           , _force :: v
                           , _mass  :: Scalar v
                           }
-- .. standalone Show and Eq omitted 
initParticle :: AdditiveGroup v => Point v -> Particle v
initParticle p = Particle p zeroV zeroV unitScalar

unitScalar = undefined

-- Should always be true:
testInit :: Point (Double,Double) -> Bool
testInit p = ((_mass (initParticle p)) == 1::Double)

How can I define 'unitScalar' above?

Was it helpful?

Solution

It is not possible with the context that you define; since you say that v is an AdditiveGroup, that's all it can be, and thus no additional properties can be attributed to v.

You can of course define additional classes that define an unit value:

{-# LANGUAGE FlexibleContexts #-}
module Main (main) where

import Data.VectorSpace

If we want to be mathematically rigorous, we'd probably do the following; however, this has undesirable consequences in Haskell, because there can only be one Monoid defined for a type, so the solution below is probably better.

-- BAD SOLUTION! (Arguably)
import Data.Monoid

instance Monoid Double where
  mempty = 1
  mappend = (*)

Instead, we define a general MultiplicativeGroup, which also is the monoid of multiplication.

class MultiplicativeGroup a where
  unit :: a
  multiply :: a -> a -> a
  reciprocal :: a -> a

instance MultiplicativeGroup Double where
  unit = 1
  multiply = (*)
  reciprocal = (1 /)

Now we can implement the working example:

data Particle v =
  Particle -- Removed Point for this example since I don't have its definition
  { _vel :: v
  , _force :: v
  , _mass :: Scalar v
  }

-- We need VectorSpace because there needs to be a Scalar type associated with v
-- Also, this context requires you to use FlexibleContexts, which should be
-- harmless
initParticle :: (VectorSpace v, MultiplicativeGroup (Scalar v))
             => Particle v
initParticle = Particle zeroV zeroV unit

-- Works as expected:
main :: IO ()
main = print $ ((_mass (initParticle :: Particle Double)) == (1::Double))

By the way, you can of course replace the MultiplicativeGroup with Num and replace unit with 1, but that'll give you far more features than what you might have wanted.

PS. You should have a look at the excellent algebra package, which does this kind of stuff more rigorously for you. Implementing force solvers on top of it wouldn't be too hard.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top