Skip to content

Instantly share code, notes, and snippets.

@n4to4
Created August 22, 2016 14:56
Show Gist options
  • Save n4to4/0997a4b422a1c4c99a8cd96df3d8f00c to your computer and use it in GitHub Desktop.
Save n4to4/0997a4b422a1c4c99a8cd96df3d8f00c to your computer and use it in GitHub Desktop.
ScalaExercisesShapeless
import shapeless._
import scala.language.implicitConversions
// object Main extends App {
// }
object HeterogenousLists extends App {
import poly._
object choose extends (Set ~> Option) {
def apply[T](s: Set[T]) = s.headOption
}
val sets = Set(1) :: Set("foo") :: HNil
val opts = sets map choose
assert(opts == Some(1) :: Some("foo") :: HNil)
import poly.identity
val l = (23 :: "foo" :: HNil) :: HNil :: (true :: HNil) :: HNil
assert((l flatMap identity) == (23 :: "foo" :: true :: HNil))
}
object PolymorphicFunctionValues {
import poly._
object choose extends (Set ~> Option) {
def apply[T](s: Set[T]) = s.headOption
}
assert(choose(Set(1, 2, 3)) == Some(1))
assert(choose(Set('a', 'b', 'c')) == Some('a'))
def pairApply(f: Set ~> Option) = (f(Set(1, 2, 3)), f(Set('a', 'b', 'c')))
assert(pairApply(choose) == ((Some(1), Some('a'))))
assert((List(Set(1, 2, 3), Set(2, 4, 6)) map choose) == (List(Some(1), Some(2))))
object size extends Poly1 {
implicit def caseInt = at[Int](x => 1)
implicit def caseString = at[String](_.length)
implicit def caseTuple[T, U](implicit st: Case.Aux[T, Int], su: Case.Aux[U, Int]) =
at[(T, U)](t => size(t._1) + size(t._2))
}
assert(size(23) == 1)
assert(size("foo") == 3)
assert(size((23, "foo")) == 4)
assert(size(((23, "foo"), 13)) == 5)
}
object TypeSafeCast {
import syntax.typeable._
val l: Any = List(Vector("foo", "bar", "baz"), Vector("wibble"))
assert(l.cast[List[Vector[String]]] == Some(l))
assert(l.cast[List[Vector[Int]]] == None)
assert(l.cast[List[List[String]]] == None)
val `List[String]` = TypeCase[List[String]]
val `List[Int]` = TypeCase[List[Int]]
val ll = List(1, 2, 3)
val result = (ll: Any) match {
case `List[String]`(List(s, _*)) => s.length
case `List[Int]`(List(i, _*)) => i + 1
}
assert(result == 2)
}
object Lazy {
sealed trait List[+T]
case class Cons[T](hd: T, tl: List[T]) extends List[T]
sealed trait Nil extends List[Nothing]
case object Nil extends Nil
trait Show[T] {
def apply(t: T): String
}
object Show {
// Base case for Int
implicit def showInt: Show[Int] = new Show[Int] {
def apply(t: Int) = t.toString
}
// Base case for Nil
implicit def showNil: Show[Nil] = new Show[Nil] {
def apply(t: Nil) = "Nil"
}
// Case for Cons[T]: note (mutually) recursive implicit argument referencing Show[List[T]]
implicit def showCons[T](implicit st: Lazy[Show[T]], sl: Lazy[Show[List[T]]]): Show[Cons[T]] = new Show[Cons[T]] {
def apply(t: Cons[T]) = s"Cons(${show(t.hd)(st.value)}, ${show(t.tl)(sl.value)})"
}
// Case for List[T]: note (mutually) recursive implicit argument referencing Show[Cons[T]]
implicit def showList[T](implicit sc: Lazy[Show[Cons[T]]]): Show[List[T]] = new Show[List[T]] {
def apply(t: List[T]) = t match {
case n: Nil => show(n)
case c: Cons[T] => show(c)(sc.value)
}
}
}
def show[T](t: T)(implicit s: Show[T]) = s(t)
val l: List[Int] = Cons(1, Cons(2, Cons(3, Nil)))
assert(show(l) == "Cons(1, Cons(2, Cons(3, Nil)))")
}
object AutoTypeclassDerivation {
trait Monoid[T] {
def zero: T
def append(a: T, b: T): T
}
object Monoid extends ProductTypeClassCompanion[Monoid] {
def mzero[T](implicit mt: Monoid[T]) = mt.zero
implicit def booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] {
def zero = false
def append(a: Boolean, b: Boolean) = a || b
}
implicit def intMonoid: Monoid[Int] = new Monoid[Int] {
def zero = 0
def append(a: Int, b: Int) = a + b
}
implicit def doubleMonoid: Monoid[Double] = new Monoid[Double] {
def zero = 0.0
def append(a: Double, b: Double) = a + b
}
implicit def stringMonoid: Monoid[String] = new Monoid[String] {
def zero = ""
def append(a: String, b: String) = a + b
}
object typeClass extends ProductTypeClass[Monoid] {
def emptyProduct = new Monoid[HNil] {
def zero = HNil
def append(a: HNil, b: HNil) = HNil
}
def product[F, T <: HList](mh: Monoid[F], mt: Monoid[T]) = new Monoid[F :: T] {
def zero = mh.zero :: mt.zero
def append(a: F :: T, b: F :: T) = mh.append(a.head, b.head) :: mt.append(a.tail, b.tail)
}
def project[F, G](instance: => Monoid[G], to: F => G, from: G => F) = new Monoid[F] {
def zero = from(instance.zero)
def append(a: F, b: F) = from(instance.append(to(a), to(b)))
}
}
}
trait MonoidSyntax[T] {
def |+|(b: T): T
}
object MonoidSyntax {
implicit def monoidSyntax[T](a: T)(implicit mt: Monoid[T]): MonoidSyntax[T] = new MonoidSyntax[T] {
def |+|(b: T) = mt.append(a, b)
}
}
// A pair of arbitrary case classes
case class Foo(i: Int, s: String)
case class Bar(b: Boolean, s: String, d: Double)
import MonoidSyntax._
import Monoid.typeClass._
val fooCombined = Foo(13, "foo") |+| Foo(23, "bar")
assert(fooCombined == Foo(36, "foobar"))
val barCombined = Bar(true, "foo", 1.0) |+| Bar(false, "bar", 3.0)
assert(barCombined == Bar(true, "foobar", 4.0))
}
object Lenses {
// A pair of ordinary case classes ...
case class Address(street: String, city: String, postcode: String)
case class Person(name: String, age: Int, address: Address)
// Some lenses over Person/Address ...
val nameLens = lens[Person] >> 'name
val ageLens = lens[Person] >> 'age
val addressLens = lens[Person] >> 'address
val streetLens = lens[Person] >> 'address >> 'street
val cityLens = lens[Person] >> 'address >> 'city
val postcodeLens = lens[Person] >> 'address >> 'postcode
val person = Person("Joe Grey", 37, Address("Southover Street", "Brighton", "BN2 9UA"))
assert(ageLens.get(person) == 37)
val updatedPerson = ageLens.set(person)(38)
assert(updatedPerson.age == 38)
val updatedPerson2 = ageLens.modify(person)(_ + 1)
assert(updatedPerson2.age == 38)
assert(streetLens.get(person) == "Southover Street")
val updatedPerson3 = streetLens.set(person)("Montpelier Road")
assert(updatedPerson3.address.street == "Montpelier Road")
}
object MainGeneric {
case class Foo(i: Int, s: String, b: Boolean)
val fooGen = Generic[Foo]
val foo = Foo(23, "foo", true)
val l = fooGen.to(foo)
assert(l == 23 :: "foo" :: true :: HNil)
val r = 13 :: l.tail
val newFoo = fooGen.from(r)
assert(newFoo.i == 13)
import poly._
sealed trait Tree[T]
case class Leaf[T](t: T) extends Tree[T]
case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T]
object inc extends -> ((i: Int) => i + 1)
val tree: Tree[Int] =
Node(
Leaf(1),
Node(
Leaf(2),
Leaf(3)
)
)
assert(everywhere(inc)(tree) == Node(
Leaf(2),
Node(
Leaf(3),
Leaf(4)
)
))
import record._
case class Book(author: String, title: String, id: Int, price: Double)
val bookGen = LabelledGeneric[Book]
val tapl = Book("Benjamin Pierce", "Types and Programming Languages", 262162091, 44.11)
val rec = bookGen.to(tapl)
assert(rec('price) == 44.11)
val updatedBook = bookGen.from(rec.updateWith('price)(_ + 2.0))
assert(updatedBook.price == 46.11)
import syntax.singleton._
case class ExtendedBook(author: String, title: String, id: Int, price: Double, inPrint: Boolean)
val bookExtGen = LabelledGeneric[ExtendedBook]
val extendedBook = bookExtGen.from(rec + ('inPrint ->> true))
assert(extendedBook.inPrint == true)
}
object Coproducts {
import shapeless._, poly._
object sizeM extends Poly1 {
implicit def caseInt = at[Int](i => (i, i))
implicit def caseString = at[String](s => (s, s.length))
implicit def caseBoolean = at[Boolean](b => (b, 1))
}
type ISB = Int :+: String :+: Boolean :+: CNil
val isb = Coproduct[ISB]("foo")
val m = isb map sizeM
assert(m.select[(String, Int)] == Some(("foo", 3)))
import record._, union._, syntax.singleton._
type U = Union.`'i -> String, 's -> String, 'b -> Boolean`.T
val u = Coproduct[U]('s ->> "foo")
assert(u.get('i) == None)
assert(u.get('s) == Some("foo"))
assert(u.get('b) == None)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment