Last active
July 3, 2021 17:54
-
-
Save johnynek/9426221 to your computer and use it in GitHub Desktop.
Simple example of how to write a linked-list in scala that knows its length at compile-time. This allows you to write a zip method that is always exact.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// I was reading through these examples: http://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/ | |
// and I thought it would be nice to more quickly get to something useful, to show the power of the techniques. | |
object SizedListExample { | |
// Type of all Non-negative integers | |
sealed trait Nat | |
// This is zero. | |
sealed trait _0 extends Nat | |
// Successor to some non-negative number | |
sealed trait Succ[N <: Nat] extends Nat | |
// Convert types into values: | |
trait Count[N <: Nat] { def toInt: Int } | |
def toInt[N <: Nat](implicit c: Count[N]): Int = c.toInt | |
implicit val zCount: Count[_0] = new Count[_0] { def toInt = 0 } | |
implicit def nCount[N <: Nat](implicit c: Count[N]): Count[Succ[N]] = { | |
val c_1 = toInt[N] | |
new Count[Succ[N]] { def toInt = c_1 + 1 } | |
} | |
// Make a very simple sized linked list | |
sealed trait LList[N<:Nat, +T] { | |
def fold[U](init: U)(fn: (U, T) => U): U | |
// map preserves length, not flatMap/filter don't work | |
def map[U](fn: T => U): LList[N, U] | |
// Note: zip requires a list of the same length | |
def zip[U](that: LList[N, U]): LList[N, (T, U)] | |
// Note: size is one bigger after this | |
def ::[U>:T](h: U): LList[Succ[N], U] | |
} | |
final case object LNil extends LList[_0, Nothing] { | |
def fold[U](init: U)(fn: (U, Nothing) => U) = init | |
def map[U](fn: Nothing => U) = LNil | |
def zip[U](that: LList[_0, U]) = LNil | |
def ::[U>:Nothing](h: U) = Cons(h, LNil) | |
} | |
final case class Cons[N<:Nat,+T](head: T, tail: LList[N, T]) extends LList[Succ[N], T] { | |
def fold[U](init: U)(fn: (U, T) => U) = tail.fold(fn(init, head))(fn) | |
def map[U](fn: T => U) = Cons(fn(head), tail.map(fn)) | |
def zip[U](that: LList[Succ[N], U]) = { | |
// How to remove this? it can never fail, there is only one subclass of LList[Succ[N], U] | |
val thatC = that.asInstanceOf[Cons[N, U]] | |
Cons((head, thatC.head), tail.zip(thatC.tail)) | |
} | |
def ::[U>:T](h: U) = Cons(h, this) | |
} | |
} | |
/****** | |
scala> import SizedListExample._ | |
import SizedListExample._ | |
scala> 1 :: 2 :: 3 :: LNil | |
res19: SizedListExample.Cons[SizedListExample.Succ[SizedListExample.Succ[SizedListExample._0]],Int] = Cons(1,Cons(2,Cons(3,LNil))) | |
scala> "hey" :: "you" :: LNil | |
res20: SizedListExample.Cons[SizedListExample.Succ[SizedListExample._0],java.lang.String] = Cons(hey,Cons(you,LNil)) | |
scala> res19 zip res20 | |
<console>:31: error: type mismatch; | |
found : SizedListExample.Cons[SizedListExample.Succ[SizedListExample._0],java.lang.String] | |
required: SizedListExample.LList[SizedListExample.Succ[SizedListExample.Succ[SizedListExample.Succ[SizedListExample._0]]],?] | |
res19 zip res20 | |
^ | |
scala> res19 zip ("fixed" :: res20) | |
res22: SizedListExample.Cons[SizedListExample.Succ[SizedListExample.Succ[SizedListExample._0]],(Int, java.lang.String)] = Cons((1,fixed),Cons((2,hey),Cons((3,you),LNil))) | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment