Project setup
This commit is contained in:
commit
131887d606
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.bsp/
|
||||
.idea/
|
||||
target/
|
||||
project/target/
|
16
build.sbt
Normal file
16
build.sbt
Normal file
@ -0,0 +1,16 @@
|
||||
addCompilerPlugin("org.typelevel" % "kind-projector" % "0.13.0" cross CrossVersion.full)
|
||||
|
||||
lazy val root = (project in file(".")).
|
||||
settings(
|
||||
inThisBuild(List(
|
||||
organization := "com.example",
|
||||
scalaVersion := "2.12.9"
|
||||
)),
|
||||
name := "scalatest-example",
|
||||
scalacOptions ++= Seq(
|
||||
"-language:implicitConversions",
|
||||
"-language:higherKinds",
|
||||
)
|
||||
)
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.9" % Test
|
1
project/build.properties
Normal file
1
project/build.properties
Normal file
@ -0,0 +1 @@
|
||||
sbt.version=1.5.5
|
19
src/main/scala/conversions/package.scala
Normal file
19
src/main/scala/conversions/package.scala
Normal file
@ -0,0 +1,19 @@
|
||||
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?
|
||||
}
|
33
src/main/scala/data/LispList.scala
Normal file
33
src/main/scala/data/LispList.scala
Normal file
@ -0,0 +1,33 @@
|
||||
package data
|
||||
|
||||
/**
|
||||
* A Lisply-named representation of a singly-linked list and an analogue to
|
||||
* Scala's immutable List. This is a special case of any type A where there
|
||||
* exists a nondeterministic selection of its values that must be specially
|
||||
* handled such that the number of values is inconsequential.
|
||||
*/
|
||||
sealed trait LispList[+A] {
|
||||
|
||||
def car: A
|
||||
|
||||
def cdr: LispList[A]
|
||||
}
|
||||
|
||||
case class LispCons[+A](car: A, cdr: LispList[A]) extends LispList[A]
|
||||
|
||||
case object LispNil extends LispList[Nothing] {
|
||||
|
||||
def car: Nothing = throw new Exception("LispList with no car!")
|
||||
|
||||
def cdr: LispList[Nothing] = throw new Exception("LispList with no cdr!")
|
||||
}
|
||||
|
||||
object LispList {
|
||||
|
||||
def apply[A](): LispList[A] = LispNil
|
||||
|
||||
def apply[A](items: A*): LispList[A] = items.foldRight(LispList[A]())(LispCons[A])
|
||||
|
||||
// TODO how to make a Seq
|
||||
// def unapplySeq[A](list: LispList[A]): Option[Seq[A]] = Some(list.toSeq)
|
||||
}
|
18
src/main/scala/data/Schrodinger.scala
Normal file
18
src/main/scala/data/Schrodinger.scala
Normal file
@ -0,0 +1,18 @@
|
||||
package data
|
||||
|
||||
/**
|
||||
* Schrodinger is an analogue to Scala's Option and a special case of
|
||||
* Superposition where a cat may or may not exist and the cat must be specially
|
||||
* handled as if it were simultaneously alive or dead.
|
||||
*/
|
||||
sealed trait Schrodinger[+A] {
|
||||
|
||||
def cat: A
|
||||
}
|
||||
|
||||
case class Alive[+A](cat: A) extends Schrodinger[A]
|
||||
|
||||
case object Dead extends Schrodinger[Nothing] {
|
||||
|
||||
def cat: Nothing = throw new Exception("He's dead Jim!")
|
||||
}
|
27
src/main/scala/data/Superposition.scala
Normal file
27
src/main/scala/data/Superposition.scala
Normal file
@ -0,0 +1,27 @@
|
||||
package data
|
||||
|
||||
/**
|
||||
* 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
|
||||
* specially as though it could be either one.
|
||||
*/
|
||||
sealed trait Superposition[+A, +B]
|
||||
|
||||
/**
|
||||
* Implied negative case of the Superposition. This is probably not the one you
|
||||
* 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]
|
||||
|
||||
/**
|
||||
* 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]
|
17
src/main/scala/data/package.scala
Normal file
17
src/main/scala/data/package.scala
Normal file
@ -0,0 +1,17 @@
|
||||
package object data {
|
||||
|
||||
/**
|
||||
* A modeled stateful computation that for any state of type S there exists
|
||||
* a computation that derives from S a product of type A and a successive
|
||||
* instance of S representing the state of the next computation.
|
||||
*
|
||||
* @tparam S The state type
|
||||
* @tparam A The value returned by the operation
|
||||
*/
|
||||
type State[S, +A] = S => (S, A)
|
||||
|
||||
/**
|
||||
* A special case of any type A that is just itself.
|
||||
*/
|
||||
type Identity[+A] = A
|
||||
}
|
8
src/main/scala/extensions/flatMap/package.scala
Normal file
8
src/main/scala/extensions/flatMap/package.scala
Normal file
@ -0,0 +1,8 @@
|
||||
package extensions
|
||||
|
||||
/**
|
||||
* Implicit method extensions for flatMap().
|
||||
*/
|
||||
package object flatMap {
|
||||
|
||||
}
|
29
src/main/scala/extensions/map/package.scala
Normal file
29
src/main/scala/extensions/map/package.scala
Normal file
@ -0,0 +1,29 @@
|
||||
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] = ???
|
||||
}
|
||||
}
|
8
src/main/scala/extensions/pure/package.scala
Normal file
8
src/main/scala/extensions/pure/package.scala
Normal file
@ -0,0 +1,8 @@
|
||||
package extensions
|
||||
|
||||
/**
|
||||
* Implicit method extensions for pure().
|
||||
*/
|
||||
package object pure {
|
||||
|
||||
}
|
24
src/main/scala/instances/identity/package.scala
Normal file
24
src/main/scala/instances/identity/package.scala
Normal file
@ -0,0 +1,24 @@
|
||||
package instances
|
||||
|
||||
import data._
|
||||
import typeclasses._
|
||||
|
||||
/**
|
||||
* Typeclass instances for Identity
|
||||
*/
|
||||
package object identity {
|
||||
|
||||
implicit val identityFunctor: Functor[Identity] = ???
|
||||
|
||||
implicit val identityApplicative: Applicative[Identity] = ???
|
||||
|
||||
implicit def identityApplicativeError[E]: ApplicativeError[Identity, E] = ???
|
||||
|
||||
implicit val identityMonad: Monad[Identity] = ???
|
||||
|
||||
implicit def identityMonadError[E]: MonadError[Identity, E] = ???
|
||||
|
||||
implicit def identitySemigroup[A]: Semigroup[Identity[A]] = ???
|
||||
|
||||
implicit def identityMonoid[A]: Monoid[Identity[A]] = ???
|
||||
}
|
20
src/main/scala/instances/lisplist/package.scala
Normal file
20
src/main/scala/instances/lisplist/package.scala
Normal file
@ -0,0 +1,20 @@
|
||||
package instances
|
||||
|
||||
import data._
|
||||
import typeclasses._
|
||||
|
||||
/**
|
||||
* Typeclass instances for LispList
|
||||
*/
|
||||
package object lisplist {
|
||||
|
||||
implicit val lispListFunctor: Functor[LispList] = ???
|
||||
|
||||
implicit val lispListApplicative: Applicative[LispList] = ???
|
||||
|
||||
implicit val lispListMonad: Monad[LispList] = ???
|
||||
|
||||
implicit def lispListMonoid[A]: Monoid[LispList[A]] = ???
|
||||
|
||||
implicit def lispListSemigroup[A]: Semigroup[LispList[A]] = ???
|
||||
}
|
24
src/main/scala/instances/schrodinger/package.scala
Normal file
24
src/main/scala/instances/schrodinger/package.scala
Normal file
@ -0,0 +1,24 @@
|
||||
package instances
|
||||
|
||||
import data._
|
||||
import typeclasses._
|
||||
|
||||
/**
|
||||
* Typeclass instances for Schrodinger
|
||||
*/
|
||||
package object schrodinger {
|
||||
|
||||
implicit val schrodingerFunctor: Functor[Schrodinger[*]] = ???
|
||||
|
||||
implicit val schrodingerApplicative: Applicative[Schrodinger[*]] = ???
|
||||
|
||||
implicit def schrodingerApplicativeError[E]: ApplicativeError[Schrodinger[*], E] = ???
|
||||
|
||||
implicit val schrodingerMonad: Monad[Schrodinger[*]] = ???
|
||||
|
||||
implicit def schrodingerMonadError[E]: MonadError[Schrodinger[*], E] = ???
|
||||
|
||||
implicit def schrodingerMonoid[A]: Monoid[Schrodinger[A]] = ???
|
||||
|
||||
implicit def schrodingerSemigroup[A]: Semigroup[Schrodinger[A]] = ???
|
||||
}
|
20
src/main/scala/instances/state/package.scala
Normal file
20
src/main/scala/instances/state/package.scala
Normal file
@ -0,0 +1,20 @@
|
||||
package instances
|
||||
|
||||
import data._
|
||||
import typeclasses._
|
||||
|
||||
/**
|
||||
* Typeclass instances for State
|
||||
*/
|
||||
package object state {
|
||||
|
||||
implicit def stateFunctor[S]: Functor[State[S, *]] = ???
|
||||
|
||||
implicit def stateApplicative[S]: Applicative[State[S, *]] = ???
|
||||
|
||||
implicit def stateApplicativeError[S, E]: ApplicativeError[State[S, *], E] = ???
|
||||
|
||||
implicit def stateMonad[S]: Monad[State[S, *]] = ???
|
||||
|
||||
implicit def stateMonadError[S, A]: MonadError[State[S, *], S] = ???
|
||||
}
|
24
src/main/scala/instances/superposition/package.scala
Normal file
24
src/main/scala/instances/superposition/package.scala
Normal file
@ -0,0 +1,24 @@
|
||||
package instances
|
||||
|
||||
import data._
|
||||
import typeclasses._
|
||||
|
||||
/**
|
||||
* Typeclass instances for Superposition
|
||||
*/
|
||||
package object superposition {
|
||||
|
||||
implicit def superpositionFunctor[A]: Functor[Superposition[A, *]] = ???
|
||||
|
||||
implicit def superpositionApplicative[A]: Applicative[Superposition[A, *]] = ???
|
||||
|
||||
implicit def superpositionApplicativeError[A]: ApplicativeError[Superposition[A, *], A] = ???
|
||||
|
||||
implicit def superpositionMonad[A]: Monad[Superposition[A, *]] = ???
|
||||
|
||||
implicit def superpositionMonadError[A]: MonadError[Superposition[A, *], A] = ???
|
||||
|
||||
implicit def superpositionMonoid[A, B]: Monoid[Superposition[A, B]] = ???
|
||||
|
||||
implicit def superpositionSemigroup[A, B]: Semigroup[Superposition[A, B]] = ???
|
||||
}
|
8
src/main/scala/ops/identity/package.scala
Normal file
8
src/main/scala/ops/identity/package.scala
Normal file
@ -0,0 +1,8 @@
|
||||
package ops
|
||||
|
||||
/**
|
||||
* Typeclass extension methods for Identity
|
||||
*/
|
||||
package object identity {
|
||||
|
||||
}
|
8
src/main/scala/ops/lisplist/package.scala
Normal file
8
src/main/scala/ops/lisplist/package.scala
Normal file
@ -0,0 +1,8 @@
|
||||
package ops
|
||||
|
||||
/**
|
||||
* Typeclass extension methods for LispList
|
||||
*/
|
||||
package object lisplist {
|
||||
|
||||
}
|
8
src/main/scala/ops/schrodinger/package.scala
Normal file
8
src/main/scala/ops/schrodinger/package.scala
Normal file
@ -0,0 +1,8 @@
|
||||
package ops
|
||||
|
||||
/**
|
||||
* Typeclass extension methods for Schrodinger.
|
||||
*/
|
||||
package object schrodinger {
|
||||
|
||||
}
|
8
src/main/scala/ops/state/package.scala
Normal file
8
src/main/scala/ops/state/package.scala
Normal file
@ -0,0 +1,8 @@
|
||||
package ops
|
||||
|
||||
/**
|
||||
* Typeclass extension methods for State.
|
||||
*/
|
||||
package object state {
|
||||
|
||||
}
|
8
src/main/scala/ops/superposition/package.scala
Normal file
8
src/main/scala/ops/superposition/package.scala
Normal file
@ -0,0 +1,8 @@
|
||||
package ops
|
||||
|
||||
/**
|
||||
* Typeclass extension methods for Superposition.
|
||||
*/
|
||||
package object superposition {
|
||||
|
||||
}
|
24
src/main/scala/typeclasses/Applicative.scala
Normal file
24
src/main/scala/typeclasses/Applicative.scala
Normal file
@ -0,0 +1,24 @@
|
||||
package typeclasses
|
||||
|
||||
/**
|
||||
* Defines the Applicative operations for any type of kind F[_]. Applicative
|
||||
* is a special case of Functor that can apply a function lifted into F[_] to
|
||||
* a value also lifted into F[_].
|
||||
*/
|
||||
trait Applicative[F[_]] extends Functor[F] {
|
||||
|
||||
/**
|
||||
* Lift a value into F[_].
|
||||
*/
|
||||
def pure[A](a: A): F[A] = ???
|
||||
|
||||
/**
|
||||
* Lift a value into F[_] and apply it to a lifted function.
|
||||
*/
|
||||
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
|
||||
|
||||
/**
|
||||
* This can be defined in terms of ap and pure alone, try it out!
|
||||
*/
|
||||
override def map[A, B](fa: F[A])(f: A => B): F[B] = ???
|
||||
}
|
25
src/main/scala/typeclasses/ApplicativeError.scala
Normal file
25
src/main/scala/typeclasses/ApplicativeError.scala
Normal file
@ -0,0 +1,25 @@
|
||||
package typeclasses
|
||||
|
||||
/**
|
||||
* Defines the ApplicativeError operations for any type of kind F[_] for error
|
||||
* type E. ApplicativeError is a special case of Applicative where the type
|
||||
* kind of F[_] may represent a superposition of "success" or "error" cases
|
||||
* and affords operations to create and recover these error cases.
|
||||
*/
|
||||
trait ApplicativeError[F[_], E] extends Applicative[F] {
|
||||
|
||||
/**
|
||||
* Lift an error handler into F[_] to apply to E.
|
||||
*/
|
||||
def handleError[A](fa: F[A])(f: E => A): F[A]
|
||||
|
||||
/**
|
||||
* Lift an error handler into F[_] to apply to E, returning a lifted result.
|
||||
*/
|
||||
def handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A]
|
||||
|
||||
/**
|
||||
* A special case of pure() which lifts an error E into F[_].
|
||||
*/
|
||||
def raiseError[A](e: E): F[A] = ???
|
||||
}
|
13
src/main/scala/typeclasses/Functor.scala
Normal file
13
src/main/scala/typeclasses/Functor.scala
Normal file
@ -0,0 +1,13 @@
|
||||
package typeclasses
|
||||
|
||||
/**
|
||||
* Defines the Functor operations for any type of kind F[_]. Functors allow for
|
||||
* a function to be applied to a value lifted into F[_].
|
||||
*/
|
||||
trait Functor[F[_]] {
|
||||
|
||||
/**
|
||||
* Applies a lifted value to a function.
|
||||
*/
|
||||
def map[A, B](fa: F[A])(f: A => B): F[B]
|
||||
}
|
17
src/main/scala/typeclasses/Monad.scala
Normal file
17
src/main/scala/typeclasses/Monad.scala
Normal file
@ -0,0 +1,17 @@
|
||||
package typeclasses
|
||||
|
||||
/**
|
||||
* Defines the Monad operations for any type of kind F[_]. A Monad is a special
|
||||
* case of Applicative where the application of a function to a lifted value
|
||||
* in F[_] may itself return a lifted value. This is especially powerful because
|
||||
* the lifted result may represent a case against which no further operations
|
||||
* may be performed, which can model short-circuiting on errors!
|
||||
*/
|
||||
trait Monad[F[_]] extends Applicative[F] {
|
||||
|
||||
/**
|
||||
* Lift an apply a function into F[_] and apply it to the value. The function
|
||||
* returns its own lifted result.
|
||||
*/
|
||||
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
|
||||
}
|
31
src/main/scala/typeclasses/MonadError.scala
Normal file
31
src/main/scala/typeclasses/MonadError.scala
Normal file
@ -0,0 +1,31 @@
|
||||
package typeclasses
|
||||
|
||||
import data.Superposition
|
||||
|
||||
/**
|
||||
* Defines the MonadOperations operations for any type of kind F[_] for error
|
||||
* type E. MonadError is a special case of ApplicativeError and Monad that
|
||||
* further augments error handling operations.
|
||||
*/
|
||||
trait MonadError[F[_], E] extends Monad[F] with ApplicativeError[F, E] {
|
||||
|
||||
/**
|
||||
* Replaces A with E if the lifted A does not satisfy the predicate.
|
||||
*/
|
||||
def ensure[A](fa: F[A])(error: => E)(predicate: A => Boolean): F[A]
|
||||
|
||||
/**
|
||||
* Replaces A with E by means of A if the lifted A does not satisfy the predicate.
|
||||
*/
|
||||
def ensureOr[A](fa: F[A])(error: A => E)(predicate: A => Boolean): F[A]
|
||||
|
||||
/**
|
||||
* Applies a partial function to the E, if any.
|
||||
*/
|
||||
def adaptError[A](fa: F[A])(pf: PartialFunction[E, E]): F[A]
|
||||
|
||||
/**
|
||||
* Inverse of ApplicativeError attempt().
|
||||
*/
|
||||
def rethrow[A, EE <: E](fa: F[Superposition[EE, A]]): F[A]
|
||||
}
|
16
src/main/scala/typeclasses/Monoid.scala
Normal file
16
src/main/scala/typeclasses/Monoid.scala
Normal file
@ -0,0 +1,16 @@
|
||||
package typeclasses
|
||||
|
||||
/**
|
||||
* A Monoid is a special case of a Semigroup for which an element of A, when
|
||||
* applied to any other element of A in any order, returns that other element.
|
||||
* It forms an identity value with which its application merely returns the
|
||||
* identity of its associated operand, similar in concept to the identity
|
||||
* function.
|
||||
*/
|
||||
trait Monoid[A] extends Semigroup[A] {
|
||||
|
||||
/**
|
||||
* The identity of A.
|
||||
*/
|
||||
def empty: A
|
||||
}
|
15
src/main/scala/typeclasses/Semigroup.scala
Normal file
15
src/main/scala/typeclasses/Semigroup.scala
Normal file
@ -0,0 +1,15 @@
|
||||
package typeclasses
|
||||
|
||||
/**
|
||||
* A semigroup is a special case of a binary operation that is both associative
|
||||
* and for any two A the operation returns another member of A. In a sense, the
|
||||
* Semigroup operation can be used from any two starting members of A to produce
|
||||
* all successive members of A.
|
||||
*/
|
||||
trait Semigroup[A] {
|
||||
|
||||
/**
|
||||
* Binary, associative operation against two of A to produce another A.
|
||||
*/
|
||||
def <>(x: A, b: A): A
|
||||
}
|
52
src/test/scala/extensions/MapSpec.scala
Normal file
52
src/test/scala/extensions/MapSpec.scala
Normal file
@ -0,0 +1,52 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user