避免将状态选择上下文向下传递到调用堆栈的“功能方式”是啥?

Posted

技术标签:

【中文标题】避免将状态选择上下文向下传递到调用堆栈的“功能方式”是啥?【英文标题】:What's the "functional way" to avoid passing state-choosing context down the call stack?避免将状态选择上下文向下传递到调用堆栈的“功能方式”是什么? 【发布时间】:2012-04-01 15:00:21 【问题描述】:

假设我有一个包含两个列表的特征。有时我对一个感兴趣,有时对另一个感兴趣。

trait ListHolder 
  val listOne = List("foo", "bar")
  val listTwo = List("bat", "baz")

我有一个函数调用链,在它的顶部我有我需要在列表之间选择的上下文,但在它的底部我使用特征。

在命令式范式中,我通过函数传递上下文:

class Imperative extends Object with ListHolder 
  def activeList(choice : Int) : List[String] = 
    choice match 
      case 1 => listOne
      case 2 => listTwo
    
  


def iTop(is : List[Imperative], choice : Int) = 
  is.mapiMiddle(_, choice)


def iMiddle(i : Imperative, choice : Int) = 
  iBottom(i, choice)


def iBottom(i : Imperative, choice : Int) = 
  i.activeList(choice)


val ps = List(new Imperative, new Imperative)
println(iTop(ps, 1)) //Prints "foo, bar" "foo,bar"
println(iTop(ps, 2)) //Prints "bat, baz" "bat, baz"

在面向对象的范式中,我可以使用内部状态来避免向下传递上下文:

class ObjectOriented extends Imperative 
  var variable = listOne


def oTop(ps : List[ObjectOriented], choice : Int) = 
  ps.map p => p.variable = p.activeList(choice) 
  oMiddle(ps)


def oMiddle(ps : List[ObjectOriented]) = oBottom(ps)

def oBottom(ps : List[ObjectOriented]) = 
  ps.map(_.variable)  //No explicitly-passed-down choice, but hidden state


val oops = List(new ObjectOriented, new ObjectOriented)

println(oTop(oops, 1))
println(oTop(oops, 2))

在函数式语言中实现类似结果的惯用方法是什么?

也就是说,我希望下面的输出与上面的输出相似。

class Functional extends Object with ListHolder
  //IDIOMATIC FUNCTIONAL CODE


def fTop(fs : List[Functional], choice : Int) = 
    //CODE NEEDED HERE TO CHOOSE LIST
    fMiddle(fs)


def fMiddle(fs : List[Functional]) = 
   //NO CHANGES ALLOWED
   fBottom(fs)


def fBottom(fs : List[Functional]) = 
  fs.map(_.activeList) //or similarly simple


def fs = List(new Functional, new Functional)

println(fTop(fs, 1))
println(fTop(fs, 2))

更新: 这会被认为是正常运行的吗?

class Functional extends Imperative with ListHolder

class FunctionalWithList(val activeList : List[String]) extends Functional

def fTop(fs : List[Functional], band : Int) = 
  fMiddle(fs.map(f => new FunctionalWithList(f.activeList(band))))


def fMiddle(fs : List[FunctionalWithList]) = 
  //NO CHANGES ALLOWED
  fBottom(fs)


def fBottom(fs : List[FunctionalWithList]) = 
  fs.map(_.activeList)


def fs = List(new Functional, new Functional)

println(fTop(fs, 1))
println(fTop(fs, 2))

【问题讨论】:

小记:不用写extends Object with ListHolder。只需写extends ListHolder(类可以扩展特征)。 【参考方案1】:

嗯,人们总是可以使用单子和单子推导来处理这种事情,但问题的核心是,不是将选择传递到堆栈中,而是将函数返回到堆栈中,直到有人知道如何解决问题就可以处理了。

def fTop(fs : List[Functional]) = 
    fMiddle(fs)


def fMiddle(fs : List[Functional]) = 
   fBottom(fs)


def fBottom(fs : List[Functional]) = 
 (choice: Int) => fs map (_ activeList choice)

然后

println(fTop(fs)(1))

一旦你开始为这类事物开发模式,你就会得到各种各样的 monad(每种 monad 代表一个特定的模式)。

【讨论】:

我实际上不会在这里推荐一个 monad,但是代码太优雅了,我不得不 +1... “问题的核心在于,不是将选择传递到堆栈中,而是将函数返回到堆栈中,”我认为这是我的老 OOP 头脑一次又一次地错过的核心概念。 @LarryOBrien 我必须编写两次代码才能正确。就像外语一样:翻译成外语/从外语翻译出来比说外语要困难得多。【参考方案2】:

您的第一个命令式版本在我看来是最实用的。

通常,Reader monad 和 State monad 转换器用于将上下文或状态向下传递到调用堆栈。

请参阅 Scalaz state monad examples 以了解状态 monad 的示例,并参阅此 scalaz 邮件 list thread 以获取类似的问题和答案。

【讨论】:

【参考方案3】:

我认为您在“更新”下的回答非常好。是的,你可以在这里使用 Reader monad。但是,当您有一个不使用 monad 的完美解决方案时,何必费心呢?

Daniel 的一元解决方案既漂亮又优雅,但是您会发现,如果方法 fTopfMiddle 开始变得更加复杂,则需要大量额外的语法来“跳过”缺失的参数。

我认为使用class 来存储上下文是合适的,因为:

这就是类的用途:在函数之间共享上下文。

Scala 的类语法比 monad 好得多。

【讨论】:

【参考方案4】:

Reader monad 可能会有所帮助。见Tony Morris's blog

【讨论】:

以上是关于避免将状态选择上下文向下传递到调用堆栈的“功能方式”是啥?的主要内容,如果未能解决你的问题,请参考以下文章

将 prop 从组件传递到堆栈导航器

合并复杂对象时堆栈红色区域的访问无效

是否有将上下文数据传递给@Asynchronous ejb调用的干净方法?

技术杂记

如何在复杂的导航堆栈之间传递变量

结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程