Compare commits

..

No commits in common. "main" and "presentation-02" have entirely different histories.

46 changed files with 385 additions and 279 deletions

2
.scalafmt.conf Normal file
View File

@ -0,0 +1,2 @@
version = 2.5.0
maxColumn = 120

View File

@ -0,0 +1,6 @@
package conversions
/**
* Implicit conversions for Identity.
*/
package object identity {}

View File

@ -0,0 +1,21 @@
package conversions
import data.{Cons, LispList, Nil}
import scala.annotation.tailrec
/**
* Implicit conversions for LispList.
*/
package object lisplist {
implicit def lispListToList[A](lispList: LispList[A]): List[A] = {
@tailrec
def go(acc: List[A], l: LispList[A]): List[A] =
l match {
case Cons(car, cdr) => go(car +: acc, cdr)
case Nil => acc
}
go(List(), lispList).reverse
}
}

View File

@ -1,19 +0,0 @@
import data._
/**
* Implicit conversions
*/
package object conversions {
/**
* Wholesale conversion of a LispList to a Scala Seq
*/
implicit def convertToSeq[A](list: LispList[A]): Seq[A] = ???
/**
* Wholesale conversion of a LispList to a Scala List
*/
implicit def convertToList[A](list: LispList[A]): List[A] = ???
// TODO what other conversions?
}

View File

@ -0,0 +1,6 @@
package conversions
/**
* Implicit conversions for Schrodinger.
*/
package object schrodinger {}

View File

@ -0,0 +1,6 @@
package conversions
/**
* Implicit conversions for State.
*/
package object state {}

View File

@ -0,0 +1,6 @@
package conversions
/**
* Implicit conversions for Superposition.
*/
package object superposition {}

View File

@ -1,3 +1,6 @@
/**
* DO NOT MODIFY THIS FILE. All exercises are performed using implicits only.
*/
package data package data
/** /**
@ -13,9 +16,9 @@ sealed trait LispList[+A] {
def cdr: LispList[A] def cdr: LispList[A]
} }
case class LispCons[+A](car: A, cdr: LispList[A]) extends LispList[A] case class Cons[+A](car: A, cdr: LispList[A]) extends LispList[A]
case object LispNil extends LispList[Nothing] { case object Nil extends LispList[Nothing] {
def car: Nothing = throw new Exception("LispList with no car!") def car: Nothing = throw new Exception("LispList with no car!")
@ -24,10 +27,7 @@ case object LispNil extends LispList[Nothing] {
object LispList { object LispList {
def apply[A](): LispList[A] = LispNil def apply[A](items: A*): LispList[A] = items.foldRight(LispList[A]())(Cons[A])
def apply[A](items: A*): LispList[A] = items.foldRight(LispList[A]())(LispCons[A]) def apply[A](): LispList[A] = Nil
// TODO how to make a Seq
// def unapplySeq[A](list: LispList[A]): Option[Seq[A]] = Some(list.toSeq)
} }

View File

@ -1,3 +1,6 @@
/**
* DO NOT MODIFY THIS FILE. All exercises are performed using implicits only.
*/
package data package data
/** /**
@ -10,8 +13,14 @@ sealed trait Schrodinger[+A] {
def cat: A def cat: A
} }
/**
* The cat is here for pets and he is fluffy.
*/
case class Alive[+A](cat: A) extends Schrodinger[A] case class Alive[+A](cat: A) extends Schrodinger[A]
/**
* :(
*/
case object Dead extends Schrodinger[Nothing] { case object Dead extends Schrodinger[Nothing] {
def cat: Nothing = throw new Exception("He's dead Jim!") def cat: Nothing = throw new Exception("He's dead Jim!")

View File

@ -1,27 +1,33 @@
/**
* DO NOT MODIFY THIS FILE. All exercises are performed using implicits only.
*/
package data package data
/** /**
* Superposition is an analogue to Scala's Either and \a special case of a * Superposition is an analogue to Scala's Either and a special case of a
* product (any type of A × B) where only A or B exists, but must be treated * Product (any type of A × B) where only A or B exists, but must be treated
* specially as though it could be either one. * specially as though it could be either one but not both.
*/ */
sealed trait Superposition[+A, +B] sealed trait Superposition[+A, +B] {
def downside: A
def upside: B
}
/** /**
* Implied negative case of the Superposition. This is probably not the one you * Implied negative case of the Superposition. This is probably not the one you
* want. * want.
*
* @param value The thing you probably don't want.
* @tparam A Downside type
* @tparam B Upside type
*/ */
case class Downside[+A, +B](value: A) extends Superposition[A, B] case class Downside[+A, +B](downside: A) extends Superposition[A, B] {
def upside = throw new Exception("I got the downside!")
}
/** /**
* Implied positive case of the Superposition. This is usually the one you want. * Implied positive case of the Superposition. This is usually the one you want.
*
* @param value The thing you usually want.
* @tparam A Downside type
* @tparam B Upside type
*/ */
case class Upside[+A, +B](value: B) extends Superposition[A, B] case class Upside[+A, +B](upside: B) extends Superposition[A, B] {
def downside = throw new Exception("I got the upside!")
}

View File

@ -1,17 +1,21 @@
/**
* DO NOT MODIFY THIS FILE. All exercises are performed using implicits only.
*/
package object data { package object data {
/** /**
* A modeled stateful computation that for any state of type S there exists * A structure for any type S for which a function exists that derives from
* a computation that derives from S a product of type A and a successive * S a product of a value of itself and a value of type A. This can model
* instance of S representing the state of the next computation. * stateful computation where state S is given to a function which produces
* a potentially-changed state and the result of the computation.
* *
* @tparam S The state type * @tparam S The state type
* @tparam A The value returned by the operation * @tparam A The type returned by the operation
*/ */
type State[S, +A] = S => (S, A) type State[S, +A] = S => (S, A)
/** /**
* A special case of any type A that is just itself. * A special case of any type A that is simply itself.
*/ */
type Identity[+A] = A type Identity[+A] = A
} }

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for Applicative operations.
*/
package object applicative {}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for ApplicativeError operations.
*/
package object applicativeError {}

View File

@ -1,8 +0,0 @@
package extensions
/**
* Implicit method extensions for flatMap().
*/
package object flatMap {
}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for Functor operations.
*/
package object functor {}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for Identity.
*/
package object identity {}

View File

@ -0,0 +1,20 @@
package extensions
import data.{Cons, LispList, Nil}
/**
* Extension methods for LispList.
*/
package object lisplist {
implicit class LispListOps[A](val lispList: LispList[A]) extends AnyVal {
import conversions.lisplist._
def asList: List[A] = lispListToList(lispList)
def length: Int =
lispList match {
case Cons(_, cdr) => 1 + LispListOps(cdr).length
case Nil => 0
}
}
}

View File

@ -1,29 +0,0 @@
package extensions
import data._
/**
* Implicit method extensions for map().
*/
package object map {
implicit class LispListOps[+A](val list: LispList[A]) extends AnyVal {
def map[B](f: A => B): LispList[B] = ???
}
implicit class SuperpositionOps[+A, +B](val fallible: Superposition[A, B]) extends AnyVal {
def map[C](f: B => C): Superposition[A, C] = ???
}
implicit class SchrodingerOps[+A](val possibly: Schrodinger[A]) extends AnyVal {
def map[B](f: A => B): Schrodinger[B] = ???
}
implicit class StateOps[S, +A](val state: State[S, A]) extends AnyVal {
def map[B](f: A => B): State[S, B] = ???
}
}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for Monad operations.
*/
package object monad {}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for MonadError operations.
*/
package object monadError {}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for Monoid operations.
*/
package object monoid {}

View File

@ -1,8 +0,0 @@
package extensions
/**
* Implicit method extensions for pure().
*/
package object pure {
}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for Schrodinger.
*/
package object schrodinger {}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for Semigroup operations.
*/
package object semigroup {}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for State.
*/
package object state {}

View File

@ -0,0 +1,6 @@
package extensions
/**
* Extension methods for Superposition.
*/
package object superposition {}

View File

@ -12,7 +12,8 @@ package object schrodinger {
implicit val schrodingerApplicative: Applicative[Schrodinger[*]] = ??? implicit val schrodingerApplicative: Applicative[Schrodinger[*]] = ???
implicit def schrodingerApplicativeError[E]: ApplicativeError[Schrodinger[*], E] = ??? implicit def schrodingerApplicativeError[E]
: ApplicativeError[Schrodinger[*], E] = ???
implicit val schrodingerMonad: Monad[Schrodinger[*]] = ??? implicit val schrodingerMonad: Monad[Schrodinger[*]] = ???

View File

@ -12,7 +12,8 @@ package object state {
implicit def stateApplicative[S]: Applicative[State[S, *]] = ??? implicit def stateApplicative[S]: Applicative[State[S, *]] = ???
implicit def stateApplicativeError[S, E]: ApplicativeError[State[S, *], E] = ??? implicit def stateApplicativeError[S, E]: ApplicativeError[State[S, *], E] =
???
implicit def stateMonad[S]: Monad[State[S, *]] = ??? implicit def stateMonad[S]: Monad[State[S, *]] = ???

View File

@ -10,15 +10,19 @@ package object superposition {
implicit def superpositionFunctor[A]: Functor[Superposition[A, *]] = ??? implicit def superpositionFunctor[A]: Functor[Superposition[A, *]] = ???
implicit def superpositionApplicative[A]: Applicative[Superposition[A, *]] = ??? implicit def superpositionApplicative[A]: Applicative[Superposition[A, *]] =
???
implicit def superpositionApplicativeError[A]: ApplicativeError[Superposition[A, *], A] = ??? implicit def superpositionApplicativeError[A]
: ApplicativeError[Superposition[A, *], A] = ???
implicit def superpositionMonad[A]: Monad[Superposition[A, *]] = ??? implicit def superpositionMonad[A]: Monad[Superposition[A, *]] = ???
implicit def superpositionMonadError[A]: MonadError[Superposition[A, *], A] = ??? implicit def superpositionMonadError[A]: MonadError[Superposition[A, *], A] =
???
implicit def superpositionMonoid[A, B]: Monoid[Superposition[A, B]] = ??? implicit def superpositionMonoid[A, B]: Monoid[Superposition[A, B]] = ???
implicit def superpositionSemigroup[A, B]: Semigroup[Superposition[A, B]] = ??? implicit def superpositionSemigroup[A, B]: Semigroup[Superposition[A, B]] =
???
} }

View File

@ -1,8 +0,0 @@
package ops
/**
* Typeclass extension methods for Identity
*/
package object identity {
}

View File

@ -1,8 +0,0 @@
package ops
/**
* Typeclass extension methods for LispList
*/
package object lisplist {
}

View File

@ -1,8 +0,0 @@
package ops
/**
* Typeclass extension methods for Schrodinger.
*/
package object schrodinger {
}

View File

@ -1,8 +0,0 @@
package ops
/**
* Typeclass extension methods for State.
*/
package object state {
}

View File

@ -1,8 +0,0 @@
package ops
/**
* Typeclass extension methods for Superposition.
*/
package object superposition {
}

View File

@ -0,0 +1,56 @@
package conversions
import data.{Cons, LispList, Nil}
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
import java.io.ByteArrayOutputStream
class LispListSpec extends AnyWordSpecLike with Matchers {
"LispList" can {
/**
* Exercise: Implicit Conversions
*/
"convert types" which {
val lispList: LispList[Int] = Cons(1, Cons(2, Cons(3, Nil)))
def lengthAsList[A](list: List[A]): Int = list.length
"become a List" in {
import conversions.lisplist._
lengthAsList(lispList) shouldBe 3
}
"become a Seq, but loudly" in {
val out = new ByteArrayOutputStream()
Console.withOut(out) {
pending
//lengthAsSeq(lispList) shouldBe 3
}
out.toString should contain("THREE OF THEM!")
}
}
/**
* Exercise: Explicit conversions with extension methods
*/
"explicitly convert types" which {
val lispList = Cons(1, Cons(2, Cons(3, Nil)))
"asList" can {
import extensions.lisplist._
"explicitly become a List" in {
lispList.asList shouldBe List(1, 2, 3)
}
}
"asSeq" can {
"explicitly become a Seq" in {
pending
//lispList.asList should eq(Seq(1, 2, 3))
}
}
}
}
}

View File

@ -0,0 +1,48 @@
package extensions
import data.{Cons, Nil}
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
class LispListSpec extends AnyWordSpecLike with Matchers {
"LispList" can {
/**
* Exercise: Create a length method
*/
"length" which {
"returns 0 if the list is Nil" in {
import extensions.lisplist._
val lispList = Nil
lispList.length shouldBe 0
}
"returns the length of the list" in {
import conversions.lisplist._
val lispList = Cons(1, Cons(2, Cons(3, Nil)))
lispList.map(_ * 2) shouldBe 0
}
}
/**
* Exercise: Create a schrodingersCar method
*/
"schrodingersCar" which {
"returns Dead" when {
"list is Nil" in {
val lispList = Nil
pending
//lispList.schrodingersCar should eq(Dead)
}
}
"returns Alive containing the first value" when {
"the list is not Nil" in {
val lispList = Cons("Ferrari", Cons("Lamborghini", Cons("Honda Fit", Nil)))
pending
//lispList.schrodingersCar should eq(Alive("Ferrari"))
}
}
}
}
}

View File

@ -1,52 +0,0 @@
package extensions
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
class MapSpec extends AnyWordSpecLike with Matchers {
"ConsList" can {
"map" which {
"transforms every element it contains" in {
pending
}
}
}
"Superposition" can {
"map" which {
"ignores the value of Downside" in {
pending
}
"transforms the value of Upside" in {
pending
}
}
}
"Schrodinger" can {
"map" which {
"ignores Dead cat" in {
pending
}
"transforms Alive cat" in {
pending
}
}
}
"State" can {
"map" which {
"transforms the result of a stateful computation" in {
pending
}
}
}
"Identity" can {
"map" which {
"transforms itself" in {
pending
}
}
}
}