Skip to content

Instantly share code, notes, and snippets.

@MaxwellBo
Last active July 16, 2019 06:52
Show Gist options
  • Select an option

  • Save MaxwellBo/eb124e54309b32305671eb1776369299 to your computer and use it in GitHub Desktop.

Select an option

Save MaxwellBo/eb124e54309b32305671eb1776369299 to your computer and use it in GitHub Desktop.
implicit val x: String = "up here"
def implicitString(implicit x: String) = x
// println(implicitString)
///////////////////////////////////////////////////////////////////////////////
implicit val booleanInstance: Boolean = true
implicit val intInstance: Int = 5
def primativeImplicitly[T](implicit x: T): T = {
x
}
// println(primativeImplicitly[Boolean])
// println(primativeImplicitly[Int])
///////////////////////////////////////////////////////////////////////////////
implicit val listIntInstance: List[Int] = List(1)
implicit val optionInstance: Option[Int] = Some(1)
def hktImplicitly[F[_]](implicit x: F[Int]): F[Int] = {
x
}
// scala> :k List
// List's kind is F[+A]
// println(hktImplicitly[List])
// println(hktImplicitly[Option])
///////////////////////////////////////////////////////////////////////////////
type Id[A] = A
// * -> *
implicit val listStringInstance: List[String] = List("hello")
implicit val listBoolInstance: List[Boolean] = List(true)
def hktAppImplicitly[F[_], A](implicit x: F[A]): F[A] = {
x
}
// println(hktAppImplicitly[List, String])
// println(hktAppImplicitly[List, Boolean])
// println(hktAppImplicitly[Id, String])
///////////////////////////////////////////////////////////////////////////////
// generally good practise to wrap implicit instances in an object so you can conditionally import their extension methods
object IntSyntax {
implicit final class IntExtensions(private val self: Int) extends AnyVal {
def increment(): Int = self + 1
}
}
// https://kotlinlang.org/docs/reference/extensions.html
import IntSyntax._
// println(5.increment()) // 6
///////////////////////////////////////////////////////////////////////////////
case class Json(innerString: String)
trait Encode[A] {
def encode(x: A): Json
}
object EncodeInstances {
implicit val encodeString: Encode[String] = new Encode[String] {
override def encode(x: String) = Json("\"" + x.toString() + "\"")
}
implicit val encodeInt: Encode[Int] = new Encode[Int] {
override def encode(x: Int) = Json(x.toString())
}
implicit val encodeBoolean: Encode[Boolean] = new Encode[Boolean] {
override def encode(x: Boolean) = Json(x.toString())
}
implicit def encodeMap[A, B]: Encode[Map[String, Json]] = new Encode[Map[String, Json]] {
override def encode(kv: Map[String, Json]) = {
val inner =
kv
.toList
.map { case (k, v) => s"${encodeString.encode(k).innerString}: ${v.innerString}" }
.mkString(", ")
val outer = s"{ ${inner} }"
Json(outer)
}
}
}
////////////////////////////////////////////////////////////////////////////////
implicit class EncodeSyntax[A](private val self: A) extends AnyVal {
def encode()(implicit instance: Encode[A]): Json = {
instance.encode(self)
}
}
import EncodeInstances._
import EncodeSyntax._
case class Person(name: String, age: Int, alive: Boolean)
implicit def encodePerson: Encode[Person] = new Encode[Person] {
override def encode(person: Person): Json =
// we can obviously do this in a macro
Map(
"name" -> person.name.encode(),
"age" -> person.age.encode(),
"alive" -> person.alive.encode(),
).encode()
}
val me = Person(name="Max Bo", age=22, alive=true)
println(me.encode().innerString)
////////////////////////////////////////////////////////////////////////////////
object Encode {
def encode[A](x: A)(implicit instance: Encode[A]): Json = {
instance.encode(x)
}
// is the same as
def encodePrime[A: Encode](x: A): Json = {
// ^ implicit Encode[A] in scope (desugars to definition above)
val instance = implicitly[Encode[A]]
instance.encode(x)
}
}
////////////////////////////////////////////////////////////////////////////////
case class NoEncoder()
def needsAnEncoder[A: Encode](a: A) {
println(a.encode().innerString)
}
// needsAnEncoder(me)
// hkts.sc:150: could not find implicit value for evidence parameter of type ammonite.$file.hkts.Encode[ammonite.$file.hkts.NoEncoder]
// val res_28 = needsAnEncoder(NoEncoder())
// needsAnEncoder(NoEncoder())
////////////////////////////////////////////////////////////////////////////////
// what if we want the `.map` to work on not just lists, but anything?
case class Foo[T](x: T)
trait Functor[F[_]] {
def fmap[A, B](fa: F[A])(f: A => B): F[B]
}
object Functor {
def fmap[F[_], A, B](fa: F[A], f: A => B)(implicit instance: Functor[F]): F[B] = {
instance.fmap(fa)(f)
}
}
object FunctorInstances {
// @ List(1, 2, 3).map(x => x + 1)
// res0: List[Int] = List(2, 3, 4)
implicit val listInstance: Functor[List] = new Functor[List] {
def fmap[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f)
}
// our custom function for our container (we can define Functor instances for anything we want!)
implicit val fooInstance: Functor[Foo] = new Functor[Foo] {
def fmap[A, B](fa: Foo[A])(f: A => B): Foo[B] = Foo(f(fa.x))
}
}
import FunctorInstances._
println(Functor.fmap(List(1, 2, 3), (x: Int) => x + 1))
println(Functor.fmap(Foo(1), (x: Int) => x + 1))
// but what if we want to do Foo(1).map((x: Int) => x + 1)?
object FunctorSyntax {
implicit final class FunctorExtensions[F[_], A](private val self: F[A]) extends AnyVal {
def fmap[B](f: A => B)(implicit instance: Functor[F]): F[B] = {
instance.fmap(self)(f)
}
}
}
import FunctorSyntax._
// println(Foo(1).fmap((x: Int) => x + 1)) // 6
///////////////////////////////////////////////////////////////////////////////
trait RowA[F[_]] {
def x: F[String]
def y: F[Int]
}
// def id(f) = f
type InsertRowA = RowA[Id]
type PatchRowA = RowA[Option]
val insert = new InsertRowA {
def x = "hello"
def y = 5
}
val patch = new PatchRowA {
def x = Some("hello")
def y = None
}
///////////////////////////////////////////////////////////////////////////////
// trait RowIncompat[F[_]] {
// def y: F[String]
// def z: F[Boolean]
// }
trait RowB[F[_]] {
def y: F[Int]
def z: F[Boolean]
}
type Join = RowA[Id] with RowB[Id]
// type Join = RowA[Id] with RowIncompat[Id]
// can't construct with typealias
val join = new RowA[Id] with RowB[Id] {
// val join = new RowA[Id] with RowIncompat[Id] {
def x = "hello"
def y = 5
def z = true
}
type Anon = { def x: String; def y: Int; def z: Boolean }
def acceptsAJoin(join: Anon) = {
println(join.x)
println(join.y)
println(join.z)
}
// acceptsAJoin(join)
type HasX = { def x: Int }
type HasY = { def y: Int }
type HasZ = { def z: Int }
case class Coordinate(x: Int, y: Int)
val origin = Coordinate(0, 0)
def acceptsX(w: HasX with HasY) = {
println(w)
}
// acceptsX(origin)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment