Skip to content

Instantly share code, notes, and snippets.

@japgolly
Created February 28, 2015 21:00
Show Gist options
  • Save japgolly/9988c96ec3dc454f6e4b to your computer and use it in GitHub Desktop.
Save japgolly/9988c96ec3dc454f6e4b to your computer and use it in GitHub Desktop.
AADT folds
object Example {
// Data types
sealed trait Base {
sealed trait Token
}
sealed trait PlainText extends Base {
case class PlainText(text: String) extends Token
}
sealed trait NewLine extends Base {
case class NewLine() extends Token
}
sealed trait Link extends Base {
case class Link(url: String) extends Token
}
sealed trait Quote extends Base {
case class Quote(content: List[Token]) extends Token
}
object BlogTitle extends PlainText with Link with Quote
object BlogComment extends PlainText with NewLine with Quote
// Folds.
// Could just as easily be Base.fold, BlogTitle.fold, BlogComment.fold
def foldAny[A](a: PlainText#PlainText => A,
b: NewLine #NewLine => A,
c: Link #Link => A,
d: Quote #Quote => A): Base#Token => A = {
case t: PlainText#PlainText => a(t)
case t: NewLine #NewLine => b(t)
case t: Link #Link => c(t)
case t: Quote #Quote => d(t)
}
def foldBlogTitle[A](a: BlogTitle.PlainText => A,
c: BlogTitle.Link => A,
d: BlogTitle.Quote => A): BlogTitle.Token => A = {
case t: BlogTitle.PlainText => a(t)
case t: BlogTitle.Link => c(t)
case t: BlogTitle.Quote => d(t)
}
def foldBlogComment[A](a: BlogComment.PlainText => A,
b: BlogComment.NewLine => A,
d: BlogComment.Quote => A): BlogComment.Token => A = {
case t: BlogComment.PlainText => a(t)
case t: BlogComment.NewLine => b(t)
case t: BlogComment.Quote => d(t)
}
}
@etorreborre
Copy link

Another encoding using "folds" (or so-called "object algebras"), where you can leave your existing Token classes outside of a Base trait:

sealed trait Token
case class  PlainText(text: String) extends Token
case object NewLine                 extends Token
case class  Link(url: String)       extends Token
case class  Quote[A](q: List[A])    extends Token

trait BlogTitleFold[X] {
  def onText(text: PlainText): X
  def onUrl(url: Link): X
  def onQuote(quote: Quote[BlogTitle]): X      
}

trait BlogTitle {
  def fold[X](folder: BlogTitleFold[X]): X
}
object BlogTitle {
   def plainText(text: PlainText): BlogTitle = new BlogTitle {
       def fold[X](folder: BlogTitleFold[X]) = folder.onText(text)
  }
  def url(u: Link): BlogTitle = new BlogTitle {
       def fold[X](folder: BlogTitleFold[X]) = folder.onUrl(u)
  }
  def quote(q: Quote[BlogTitle]) =  new BlogTitle {
       def fold[X](folder: BlogTitleFold[X]) = folder.onQuote(q)
  }
}

object Example {
   def blogTitleToText = trait BlogTitleFold[String] {
      def onText(text: PlainText) = text.text
      def onUrl(url: Link) = url.url
      def onQuote(quote: Quote[BlogTitle]) = quote.q.map(_.fold(this)).mkString(",")   
  }

  val url1 = Link("http://hello.org")
  BlogTitle.url(url1).fold(blogTitleToText)
}

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