以更实用的方式(scalaz)使用 Scala 会导致性能/可维护性损失吗?

发布于 2024-11-30 03:23:25 字数 2342 浏览 1 评论 0原文

我目前正在开发一个小项目(<10k loc),该项目主要是纯粹的,但依赖于主要基于迭代器的可变优化和一些用于重型计算的数据结构重用。

我想学习更多的函数式编程,并希望获得更多的类型安全性,例如将可变计算包装到状态转换器单子等中。为此,存在 scalaz 库。

问题一

当通过使用所有奇特的功能性东西来更大规模地抽象我的计算时,我是否会引入我无法摆脱的性能杀手?就像当我的计算深深地包裹在 Monad 中时一样?

问题二

考虑Scala 的有限类型推断是否可行?我目前正在与非常大的类型签名作斗争(也许是因为我不知道如何正确地摆脱它们)。我认为更加“实用”将会引入更多这样的样板代码。

免责声明

我并不是质疑函数式方法是好还是坏。向 Haskell 问这个问题是没有意义的。我质疑 Scala 这样做是否明智。

根据请求进行编辑:我的项目中的大型类型签名示例

(但这将是一个不同的问题)

以下代码描述了对类型参数化输入对象的迭代计算(DiscreteFactorGraph[VariableType, FactorType[VariableType]])。您可以使用createInitialState构造一个计算对象,并使用advanceState对其进行计算,最后使用marginals从中提取一些信息。

我希望在计算过程中保留因子图对象的类型(及其参数类型),以便最终应用边际值生成正确类型的 DiscreteMarginals[VariableType]。我认为目前我只需保留计算类型内的变量类型(即 TState),因此不使用携带因子类型。但在不同的地方,我什至需要 DiscreteFactorGraph 的类型是可变的,因此我倾向于在未来的计算中需要更多的类型信息。

我经常摆弄这部分,希望有更好的解决方案。目前我有一个非常实用的方法,其中只有这三个函数。但我必须通过它们链接类型。或者,我可以将其定义为一个类,并使用所有这些类型参数化该类,这样我就不必为每个方法重复类型参数。

object FloodingBeliefPropagationStepper extends SteppingGraphInferer {
  def marginals[V <: DiscreteVariable, F <: DiscreteFactor[V]](state: FloodingBeliefPropagationStepper.TState[V,F]): DiscreteMarginals[V] =
    BeliefPropagation.marginals(state._1, state._2)

  def advanceState[V <: DiscreteVariable, F <: DiscreteFactor[V]](state: FloodingBeliefPropagationStepper.TState[V,F]): FloodingBeliefPropagationStepper.TState[V,F] = {
    val graph = state._1
    (graph,
      BeliefPropagation.computeFactorMessages(
      graph,
      BeliefPropagation.computeVariableMessages(graph, state._2, graph.variables),
      graph.factors))
  }

  def createInitialState[V <: DiscreteVariable, F <: DiscreteFactor[V]](graph: DiscreteFactorGraph[V, F],
                                                                        query: Set[V],
                                                                        random: Random): FloodingBeliefPropagationStepper.TState[V,F] = {
    (graph,
      BeliefPropagation.computeFactorMessages(
      graph,
      BeliefPropagation.createInitialVariableMessages(graph, random),
      graph.factors))
  }

  type TState[V <: DiscreteVariable, F <: DiscreteFactor[V]] = (DiscreteFactorGraph[V,F],Map[(F,V),DiscreteMessage])
}

I'm currently working on a small project (< 10k loc) which is mainly pure but relies on mutable optimizations mainly based on iterators and some data-structure reuse for heavy-duty calculations.

I'd like to learn a bit more functional programming and want to get more type safety, by e.g. wrapping mutable computations into state transformer monads and the like. For this purpose there exists the scalaz library.

Question One

When abstracting my computations on a larger scale by using all the fancy functional stuff, will I introduce performance killers that I won't get rid of? Like when my calculation is wrapped deep to the knees in Monads?

Question Two

Is it feasibly at all considering Scala's limited type inference? I'm currently fighting with very large type signatures (maybe because I don't know how to properly get rid of them). I suppose that going more "functional" will introduce even more such boiler-plate code.

Disclaimer

I'm not questioning whether the functional approach is good or bad. Asking this question for Haskell is pointless. I am questioning whether it is sensible doing so for Scala.

Edit on request: example of large type signatures in my project

(but this would be a different question)

The following code describes an iterative computation on a type-parameterized input object (DiscreteFactorGraph[VariableType, FactorType[VariableType]]). You can construct a computation object with createInitialState and perform computation on it with advanceState and finally extract some information from it with marginals.

I want the type of the factor graph object (and its parameter types) to be preserved during the computation so that the final application of marginals yields the correct type of DiscreteMarginals[VariableType]. I think currently I only have to preserve the variable type inside the computation type (which is TState), so carrying around the factor type is not used. But at a different place I need even the type of DiscreteFactorGraph to be variable, so I tend to need more type information carried through the computation in the future.

I was fiddlying around with this part a lot and I hope there's some better solution. Currently I have a pretty functional approach, where there are only those three functions. But I have to chain the type through them. Alternatively I can define it as a class and parameterise the class with all those types, so I don't have to repeat the type parameters for each method.

object FloodingBeliefPropagationStepper extends SteppingGraphInferer {
  def marginals[V <: DiscreteVariable, F <: DiscreteFactor[V]](state: FloodingBeliefPropagationStepper.TState[V,F]): DiscreteMarginals[V] =
    BeliefPropagation.marginals(state._1, state._2)

  def advanceState[V <: DiscreteVariable, F <: DiscreteFactor[V]](state: FloodingBeliefPropagationStepper.TState[V,F]): FloodingBeliefPropagationStepper.TState[V,F] = {
    val graph = state._1
    (graph,
      BeliefPropagation.computeFactorMessages(
      graph,
      BeliefPropagation.computeVariableMessages(graph, state._2, graph.variables),
      graph.factors))
  }

  def createInitialState[V <: DiscreteVariable, F <: DiscreteFactor[V]](graph: DiscreteFactorGraph[V, F],
                                                                        query: Set[V],
                                                                        random: Random): FloodingBeliefPropagationStepper.TState[V,F] = {
    (graph,
      BeliefPropagation.computeFactorMessages(
      graph,
      BeliefPropagation.createInitialVariableMessages(graph, random),
      graph.factors))
  }

  type TState[V <: DiscreteVariable, F <: DiscreteFactor[V]] = (DiscreteFactorGraph[V,F],Map[(F,V),DiscreteMessage])
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

几度春秋 2024-12-07 03:23:25

关于问题一:

将计算包装到 monad、applicatives、functor 和其他函数式 vodoo 中将会产生一些开销。但将计算包装到过程、方法、对象中也是如此。

问题的核心是,计算量必须有多小,才能使环绕变得明显。在不了解项目的一些细节的情况下,没有人会告诉您这些事情。然而,由于 Scala 的混合性质,您不必一直使用 monad。很可能将类似 scalaz 的风格用于更高级别的计算组合,并在性能需要时使用本地包含的可变状态。

关于问题二:

由于我不知道您的类型签名的性质,scalaz 是否会通过泛化计算来帮助您,或者您是否必须围绕 Scala 对部分类型构造函数应用程序的有限支持进行键入。

如果您的类型签名失控,我建议您尝试在包对象中声明类型别名并使用它们。

Regarding Question One:

There will be some overhead by wrapping your computations into monads, applicatives, functors and other functional vodoo. But so does wrapping your computations into procedures, methods, objects.

The question's core is, how small the computation has to be, so that the wrapping starts to be noticeable. That's something noone will be to tell you without knowing some details of your project. However due to Scala's hybrid nature you don't have to go monads all the way down. It's quite possible to use the scalaz-like style for the higher-level compositions of your computations and use locally-contained mutable state where performance requires it.

Regarding Question Two:

Since I have no clue of the nature of your type signatures, if scalaz will help you by generalizing computations or if you will have to type your way around Scala's limited support for partial type constructor application.

If your type signatures go out of hand, I suggest that you try to declare type alias in a package object and use these.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文