Skip to content

Instantly share code, notes, and snippets.

@ppurang
Created April 20, 2011 21:51
Show Gist options
  • Save ppurang/932958 to your computer and use it in GitHub Desktop.
Save ppurang/932958 to your computer and use it in GitHub Desktop.
In 2.9.0 RC0 issues encountered while defining a map using fold on BTree
/*
Given a map defined like:
def map[A, B](f: A => B)(btree: BTree[A]): BTree[B] =
fold((_:A) => Leaf(f(_)).asInstanceOf[BTree[B]])((x:BTree[B]) => (y:BTree[B]) => Fork(x,y))(btree)
when applied on a tree ((1 2) (3 4))
map((x:Int) => x*2)(BTree(1 :: 2 :: 3 :: 4 :: Nil))
results in ((<function1> <function1>) (<function1> <function1>))
*/
sealed trait BTree[+A]
case class Leaf[+A](a: A) extends BTree[A] {
override def toString = a.toString
}
case class Fork[+A](left: BTree[A], right: BTree[A]) extends BTree[A] {
override def toString = "(" + left + " " + right + ")"
}
//but when it is defined like
object BTree {
def map[A, B](f: A => B)(btree: BTree[A]): BTree[B] =
fold((x:A) => Leaf(f(x)).asInstanceOf[BTree[B]])((x:BTree[B]) => (y:BTree[B]) => Fork(x,y))(btree)
def fold[A,B](f: A => B)(g: B => B => B)(btree: BTree[A]): B = btree match {
case Leaf(x) => f(x)
case Fork(x,y) => g(fold(f)(g)(x))(fold(f)(g)(y))
}
def apply[A](list: List[A]): BTree[A] = {
val m: Int = list.length / 2
m match {
case 0 => Leaf(list.head)
case _ => val tuple = list.splitAt(m)
Fork(BTree(tuple._1), BTree(tuple._2))
}
}
//todo varargs apply
}
//and applied like before i.e. map((x:Int) => x*2)(BTree(1 :: 2 :: 3 :: 4 :: Nil))
//results in ((2 4) (6 8)) -- the expected outcome
//also notice the hack in the definition Leaf(f(x)).asInstanceOf[BTree[B]]
//without it the compiler complains that Fork(x,y) is not correct because a Leaf's expected.
//I wanted to give the compiler a type hint so that it wouldn't complain
//but that was turning out to be rather complicated (perhaps use :type to find it out in the REPL)
@djspiewak
Copy link

So, the reason the first one returns weird results is because of this:

fold((_:A) => Leaf(f(_))

This actually means the same as the following:

fold((_:A) => (a: A) => Leaf(f(a))

As for the cast, you should be able to replace that with a simple attribution:

  def map[A, B](f: A => B)(btree: BTree[A]): BTree[B] =
    fold((x:A) => Leaf(f(x)): BTree[B])((x:BTree[B]) => (y:BTree[B]) => Fork(x,y))(btree)

Unfortunately, there's no way to allow the type inference to figure this out. Local inference is just too limited.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment