Digamos que tengo un rasgo que tiene dos listas. A veces estoy interesado en el uno, a veces en el otro.¿Cuál es la "manera funcional" de evitar pasar contexto de selección de estado por la pila de llamadas?
trait ListHolder {
val listOne = List("foo", "bar")
val listTwo = List("bat", "baz")
}
que tienen una cadena de llamadas a funciones, en la parte superior de la que tengo el contexto tengo que elegir entre las listas, pero en la parte inferior de la que usan el rasgo.
En el paradigma imperativo, paso por el contexto a través de las funciones:
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.map{iMiddle(_, 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"
En el paradigma orientado a objetos, puedo usar estado interno para evitar pasar el contexto abajo:
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))
¿Cuál es la forma idiomática de lograr un resultado similar en un lenguaje funcional?
Es decir, me gustaría que la salida de lo siguiente sea similar a la salida de la anterior.
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))
ACTUALIZACIÓN: esto se consideraría adecuadamente funcional?
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))
Nota pequeña: No tiene que escribir 'extends Object with ListHolder'. Simplemente escriba 'extends ListHolder' (las clases pueden ampliar rasgos). – Jesper