wor*_*shi 3 haskell algebra type-families
所以我正在使用Data.VectorSpace,我正在尝试扩展force-layout.
现在我想生成一个多态的'1'标量.也就是说,一个标量,如果它与一个向量相乘,将生成相同的向量,而不管该向量的类型(参数).可能吗?有没有有用的解决方法?
这是一个更具体的代码示例(它继续从我在这里使用的代码):
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)
Run Code Online (Sandbox Code Playgroud)
如何在上面定义'unitScalar'?
您定义的上下文不可能; 因为你说那v是一个AdditiveGroup,就是这样,所以没有其他属性可以归结为v.
您当然可以定义定义单位值的其他类:
{-# LANGUAGE FlexibleContexts #-}
module Main (main) where
import Data.VectorSpace
Run Code Online (Sandbox Code Playgroud)
如果我们想要在数学上严谨,我们可能会做以下事情; 但是,这在Haskell中会产生不良后果,因为只能为一个类型定义一个Monoid,所以下面的解决方案可能更好.
-- BAD SOLUTION! (Arguably)
import Data.Monoid
instance Monoid Double where
mempty = 1
mappend = (*)
Run Code Online (Sandbox Code Playgroud)
相反,我们定义了一个通用MultiplicativeGroup,它也是乘法的幺半.
class MultiplicativeGroup a where
unit :: a
multiply :: a -> a -> a
reciprocal :: a -> a
instance MultiplicativeGroup Double where
unit = 1
multiply = (*)
reciprocal = (1 /)
Run Code Online (Sandbox Code Playgroud)
现在我们可以实现工作示例:
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))
Run Code Online (Sandbox Code Playgroud)
顺便说一句,你当然可以取代MultiplicativeGroup与Num和替换unit用1,但会给你会比你想要的远远更多的功能.
PS.你应该看看优秀的algebra包装,它会为你更严格地做这种事情.在它上面实施力量求解器并不会太难.