Skip to content

Instantly share code, notes, and snippets.

@DDR0
Created November 2, 2020 06:58
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 DDR0/022f1b468ad028ae3fda561b479cbf15 to your computer and use it in GitHub Desktop.
Save DDR0/022f1b468ad028ae3fda561b479cbf15 to your computer and use it in GitHub Desktop.
import scala.annotation.tailrec
// generic version
sealed trait List[A] {
@tailrec
final def length(counter: Int=0): Int =
this match {
case Pair(_, tail:List[A]) => tail.length(counter+1)
case End() => counter
case x =>
//If it's not a pair or and end, print it for debugging.
print("Could not match ")
print(x.getClass)
print(". Is List? ")
println(x.isInstanceOf[List[A]])
-1
}
}
case class End[A]() extends List[A]
case class Pair[A](head: A, tail: List[A]) extends List[A]
Pair('A', Pair('B', Pair('C', End())))
Pair('A', Pair('B', Pair('C', End()))).length()
End().length()
println("done")
// non-generic version
sealed trait IntList extends List[Int] {
@tailrec
final def sum(counter: Int=0): Int =
this match {
case IntPair(head, tail) => tail.sum(head + counter)
case IntEnd() => counter
}
}
final case class IntEnd() extends IntList
final case class IntPair(head: Int, tail: IntList) extends IntList
val example = IntPair(1, IntPair(2, IntPair(3, IntEnd())))
assert(example.sum() == 6)
assert(example.tail.sum() == 5)
assert(IntEnd().sum() == 0)
//This fails because I can't get IntList to be a subclass of List,
//and the List function can't "see" IntList.
//The problem is that we want the IntEnd to inherit from IntList for
//the sum function, but also End so the length function can match.
//The inheritance pattern should look like this:
//
// List
// ↙ ↓ ↘
// Pair IntList End
// ↓ ↙ ↘ ↓
// IntPair IntEnd
//
//I can't figure out how to get IntEnd to inherit from End and
//IntList at the same time. Trying to inherit from case classes is
//Not A Thing™ - if we try something like
//
// > final case class IntEnd() extends End with IntList
//
//we get
//
// > case class IntEnd has case ancestor $line649.$read.$iw.$iw.End, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes.
//
//Furthermore, if we try to make an intermediate trait we can inherit
//from, like
//
// > sealed trait EndT[A] extends List[A]
//
//and use is like
//
// > case class End[A]() extends EndT[A]
//
//and something like
//
// > case class IntEnd() extends EndT[Int] with IntList
//
//then we can't match against EndT in our List's length
//implementation because it's not in scope. The same goes for
//
// > abstract case class EndT[A]() extends List[A]
// > case class End[A]() extends EndT[A]
//
//- we can match against EndT, but we can't inherit it for End.
//Now, if do the obvious and make End a normal class, inheriting
//from the abstract case class EndT, we *can* match against EndT
//and do our switch statement that way and it works with the addition
//of a couple of `new` statements. However, we cannot use this
//strategy with the Int variants' .sum function, because we need our
//objects to be both a PairT and an IntPairT for that, which they —
//being both class objects — can't be. We can't get rid of the class
//objects either, because then we can't match against them like we
//want to.
//
//At this point, I'm out of ideas.
example.length()
IntEnd().length()
println("done int")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment