Say I have a trait that has two lists. Sometimes I'm interested in the one, sometimes in t'other.
trait ListHolder {
val listOne = List("foo", "bar")
val listTwo = List("bat", "baz")
}
I have a chain of function calls, at the top of which I have the context I need to choose between the lists, but at the bottom of which I use the trait.
In the imperative paradigm, I pass down the context through the functions:
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"
In the object-oriented paradigm, I can use internal state to avoid passing the context down:
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))
What is the idiomatic way to achieve a similar outcome in a functional language?
That is, I would like the output of the following to be similar to the output from the above.
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))
UPDATE: Would this be considered properly functional?
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))
Well, one can always use monads and monadic comprehensions to get handle this kind of thing, but the heart of the matter is that instead of passing choices down the stack, you return functions up the stack, until someone who knows how to solve the problem can handle it.
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)
}
And then
println(fTop(fs)(1))
Once you start to develop patterns for this kind of thing, you end up with monads of all sorts (each kind of monad represents a particular pattern).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With