Haskell中的Runge-Kutta(RK4),类型系统问题

时间:2014-10-19 12:41:37

标签: haskell types runge-kutta

我正在尝试在Haskell中实现4阶Runge-Kutta,但我发现很难将Haskell类型系统用于此任务。有人可以帮忙吗?我希望在以下代码中将“State”和“DState”类型更改为类型类:

data State = State Double deriving (Show)
data DState = DState Double deriving (Show)

update :: State -> DState -> State
update (State x) (DState y) = State (x+y)

add :: DState -> DState -> DState
add (DState x) (DState y) = DState (x + y)

scale :: Double -> DState -> DState
scale h (DState x) = DState (h*x)


update_rk4 :: State -> (Double -> State -> DState) -> Double -> Double -> State
update_rk4 y f t h = update y (scale (h*(1.0/6.0)) s) where
  s = add k1 (add s2 (add s3 k4))
  s2 = scale 2 k2
  s3 = scale 2 k3
  k1 = f t y
  k2 = f (t+0.5*h) ( update y (scale (0.5*h) k1) )
  k3 = f (t+0.5*h) ( update y (scale (0.5*h) k2) )
  k4 = f (t+h) ( update y (scale h k3) )

似乎很难制定类型类,因为State和DState是交织在一起的,因为State的特定实例需要一个特定的DState实例。或者是否有其他一些我看不到的方法?

2 个答案:

答案 0 :(得分:9)

这不是你要为自己的类型类滚动的东西。 Haskell不是一种OO语言,你可以为所有内容创建类,而类应该捕获“深度数学概念”。在这个例子中,非常常见的“概念”是你可以添加差异变化,并确定这些类exist already

update :: (AffineSpace st, d ~ Diff st) => st -> d -> st
      -- note that idiomatic argument order would be `d -> st -> st` instead.
update = (.+^)

add :: VectorSpace d => d -> d -> d
add = (^+^)

scale :: (VectorSpace d, h ~ Scalar d)
   => h -> d -> d
scale = (*^)


update_rk4 :: (AffineSpace st, d ~ Diff st, VectorSpace d, h ~ Scalar d, Fractional h)
     => st -> (h -> st -> d) -> h -> h -> st
     -- again, more idiomatic order is `(h -> st -> d) -> h -> h -> st -> st`.

至于为什么我建议最后放置st参数:在Haskell中部分应用函数是常见的,而η-reduce是“pipelining参数”。在这个例子中,你很可能想要重用一个特定的“RK4步进器”,如果st参数是最后一个你可以用

来做到这一点。
simStep :: ParticularState -> ParticularState
simStep = update_rk4 f t h
 where f t y = ...
       ...

如果您必须在y中绑定simStep y = update_rk4 y f t h变量,则需要使用f声明对其进行隐藏,或者使用尴尬的f t y' = ...消除歧义。在这种情况下,这并不是一个很大的收获,但如果你始终如一地应用η减少的想法,它可以大大清理你的整体代码。

答案 1 :(得分:0)

您可以使用带有functional dependencies的多参数类型类,尽管这些不是标准的Haskell 98,而是GHC扩展。这些允许您定义一个类型类,其方法不会确定所有类型参数,否则这些参数将是不明确的。

例如,

class RK4 state dstate | dstate -> state where
    update :: state -> dstate -> state
    add :: dstate -> dstate -> dstate
    scale :: dstate -> dstate -> dstate

如果没有函数依赖,addscale将是不明确的,因为对它们的调用不会修复state类型,从而无法解析类型类约束。

请参阅上述链接中的更多示例,教程和讨论。另请参阅functional dependencies vs type families的比较,这是在另一个响应中采用的方法。