0

I have a ADT for binary tree:

// ADT for a binary tree
sealed trait BinaryTree[A]
case class Leaf[A](value: A) extends BinaryTree[A]
case class Branch[A](left: BinaryTree[A], right: BinaryTree[A]) extends BinaryTree[A]

How to solve the visitor pattern?

def visit[A](sideEffect: A => Unit, tree: BinaryTree[A]) = ???
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
jay
  • 11
  • 3

2 Answers2

2

In scala, this operation frequently not called visitor pattern. In general, functional constructs rather than GOF patterns used for different utility tasks such as working with collection or so, thanks to fancy libraries like cats or scalaZ. There's Functor which yields you the ability to map over data structures, and also there's Monad abstraction which adds the creation of data structure and flatMap operation. So operation map with resulting type unit will do the same thing as the "visitor".

def map[A,B](tree: BinaryTree[A], f: A => B)

Generic way of implementing map for ADT is the recursion with case exhaustion:

def map[A,B](tree: BinaryTree[A], f: A => B): BinaryTree[B]:= 
  match tree {
    case Leaf(a)     => Leaf(f(b))
    case Branch(l,r) => Branch(map(l,f), map(r,f))
  }

Exact "visitor" thing you asking for could be done with throwing of return value. and you can also add some stack safety, turining this to tail recursion, like described in this question:

How to make tree mapping tail-recursive?

You can also create instances for cats type classes to use cats syntax and abstract over this particular data structure.

Iva Kam
  • 932
  • 4
  • 13
2

It looks like you just want to walk the tree and apply a function to all the Leaf values.

def visit[A](sideEffect: A=>Unit, tree: BinaryTree[A]):Unit = tree match {
  case Leaf(v)          => sideEffect(v)
  case Branch(lft, rgt) => visit(sideEffect, lft)
                           visit(sideEffect, rgt)
}
jwvh
  • 50,871
  • 7
  • 38
  • 64