Skip to content

Instantly share code, notes, and snippets.

@afsalthaj
Created January 8, 2020 23:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afsalthaj/41fc4ca40167f6625ac86c7bf4819aeb to your computer and use it in GitHub Desktop.
Save afsalthaj/41fc4ca40167f6625ac86c7bf4819aeb to your computer and use it in GitHub Desktop.
import scalaz.State
import scalaz._, Scalaz._
import StateInfo._
// A nice functional way to produce a simple table from a tree (minimal code, in real life tree can be further nested and state becomes super important)
// Nowhere it can go wrong, it traverses back and forth the table keys and values and pads empty spaces to match the maximum size of keys and values that's encountered
// up until then.
sealed trait Tree
case class Leaf(key: String, value: String) extends Tree
case class Both(left: Tree, right: Tree) extends Tree
// A state holds max size of the keys and values and the whole list of records
case class StateInfo(key: Int, value: Int, r: List[Leaf])
object StateInfo {
def update(key: String, value: String): State[StateInfo, Unit] = {
for {
_ <- modifyState(
maxSizes =>
if (maxSizes.key < key.size)
maxSizes.copy(key = key.size)
else maxSizes
)
_ <- modifyState(
maxSizes =>
if (maxSizes.value < value.size)
maxSizes.copy(value = value.size)
else maxSizes
)
v <- getCurrentState
_ <- modifyState(
maxSizes =>
maxSizes.copy(
r = maxSizes.r.map(
leaf =>
leaf.copy(
key = leaf.key.padTo(v.key, ' '),
value = leaf.value.padTo(v.value, ' ')
)
) :+ Leaf(
key.padTo(v.key, ' '),
value.padTo(v.value, ' ')
)
)
)
} yield ()
}
// Merging two states will have to involve getting rid of an initial state
def appendTwoStates(
left: StateInfo,
right: StateInfo
): State[StateInfo, Unit] =
State.apply(
_ =>
(
right.r
.traverse { l =>
update(l.key, l.value)
}
.exec(left),
()
)
)
// Type inference issues
def modifyState(s: StateInfo => StateInfo) = {
State.modify[StateInfo](s)
}
def getCurrentState =
get[StateInfo]
}
def createTable(tree: Tree): State[StateInfo, Unit] =
tree match {
case Leaf(key, value) => StateInfo.update(key, value)
case Both(left, right) =>
for {
_ <- createTable(left)
v1 <- getCurrentState
_ <- createTable(right)
v2 <- getCurrentState
} yield StateInfo.appendTwoStates(v1, v2)
}
val tree = Both(
Both(Leaf("key", "value"), Leaf("keyInc", "valueInccccccc")),
Leaf("keyIncInc", "value")
)
val result = createTable(
Both(Both(Leaf("Keys", "Values"), Leaf("----", "----")), tree)
) exec StateInfo(0, 0, Nil)
val console =
result.r.map(l => s"| ${l.key} | ${l.value} |").mkString("\n")
println(console)
/*
| Keys | Values |
| ---- | ---- |
| key | value |
| keyInc | valueInccccccc |
| keyIncInc | value |
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment