diff --git a/src/main/scala/sierikov/codewars/kyu1/ScottEncoding.scala b/src/main/scala/sierikov/codewars/kyu1/ScottEncoding.scala new file mode 100644 index 0000000..53e6673 --- /dev/null +++ b/src/main/scala/sierikov/codewars/kyu1/ScottEncoding.scala @@ -0,0 +1,86 @@ +package sierikov.codewars.kyu1 + +object ScottEncoding { + trait STuple[+A, +B] { + def apply[C]: ((A, B) => C) => C + def _1: A = apply((a, _) => a) + def _2: B = apply((_, b) => b) + def swap: STuple[B, A] = apply((a, b) => STupleImpl(b, a)) + } + + trait SOption[+A] { + def apply[B]: (=> B, A => B) => B + } + + trait SEither[+A, +B] { + def apply[C]: (A => C, B => C) => C + } + + trait SList[+A] { + def apply[B]: (=> B, (A, SList[A]) => B) => B + } + + case class STupleImpl[A, B](a: A, b: B) extends STuple[A, B] { + override def apply[C]: ((A, B) => C) => C = f => f(a, b) + } + + def toTuple[A, B](tuple: STuple[A, B]): (A, B) = (tuple._1, tuple._2) + + def fromTuple[A, B](tuple: (A, B)): STuple[A, B] = STupleImpl(tuple._1, tuple._2) + + def fst[A, B](tuple: STuple[A, B]): A = tuple._1 + + def snd[B](tuple: STuple[_, B]): B = tuple._2 + + def swap[A, B](tuple: STuple[A, B]): STuple[B, A] = tuple.swap + + def curry[A, B, C](f: STuple[A, B] => C): A => B => C = x => y => f(fromTuple(x, y)) + + def uncurry[A, B, C](f: A => B => C): STuple[A, B] => C = tuple => f(fst(tuple))(snd(tuple)) + + def toOption[A](option: SOption[A]): Option[A] = option.apply(None, Some(_)) + + def fromOption[A](option: Option[A]): SOption[A] = new SOption[A] { + override def apply[B]: (=> B, A => B) => B = (b, a) => option.fold(b)(a) + } + + def isSome(option: SOption[_]): Boolean = option.apply(true, _ => false) + + def isNone(option: SOption[_]): Boolean = option.apply(false, _ => true) + + def catOptions[A](list: SList[SOption[A]]): SList[A] = ??? + + def toEither[A, B](either: SEither[A, B]): Either[A, B] = ??? + + def fromEither[A, B](either: Either[A, B]): SEither[A, B] = ??? + + def isLeft[A](either: SEither[A, _]): Boolean = ??? + + def isRight[A](either: SEither[A, _]): Boolean = ??? + + def nil[A]: SList[A] = ??? + + def toList[A](list: SList[A]): List[A] = ??? + + def fromList[A](list: List[A]): SList[A] = ??? + + def cons[A](head: A, list: SList[A]): SList[A] = ??? + + def concat[A](left: SList[A], right: SList[A]): SList[A] = ??? + + def empty(list: SList[_]): Boolean = ??? + + def length(list: SList[_]): Int = ??? + + def map[A, B](f: (A => B), list: SList[A]): SList[B] = ??? + + def zip[A, B](listA: SList[A], listB: SList[B]): SList[STuple[A, B]] = ??? + + def foldLeft[A, B](f: ((B, A) => B), z: B, list: SList[A]): B = ??? + + def foldRight[A, B](f: ((A, B) => B), z: B, list: SList[A]): B = ??? + + def take[A](n: Int, list: SList[A]): SList[A] = ??? + + def partition[A, B](list: SList[SEither[A, B]]): STuple[SList[A], SList[B]] = ??? +} diff --git a/src/test/scala/sierikov/codewars/kyu1/ScottEncodingSpec.scala b/src/test/scala/sierikov/codewars/kyu1/ScottEncodingSpec.scala new file mode 100644 index 0000000..cfd0a1f --- /dev/null +++ b/src/test/scala/sierikov/codewars/kyu1/ScottEncodingSpec.scala @@ -0,0 +1,74 @@ +package sierikov.codewars.kyu1 + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ScottEncodingSpec extends AnyFlatSpec with Matchers { + + import ScottEncoding._ + + "The Option type".can("be cast to scala Option") in { + toOption(new SOption[Int] { + def apply[B] = (z, _) => z + }) should be(None) + toOption(new SOption[Int] { + def apply[B] = (_, f) => f(4) + }) should be(Some(4)) + } + it.can("be cast from scala Option") in { + fromOption[Int](None)[Int](0, _ + 1) should be(0) + fromOption(Some(4))[Int](0, _ + 1) should be(5) + } + + it.can("be predicated on its content") in { + isSome(new SOption[Int] { + def apply[B] = (_, f) => f(4) + }) should be(true) + isSome(new SOption[Int] { + def apply[B] = (z, _) => z + }) should be(false) + } + + "The List type".can("be cast to scala List") in { + toList(nil[Int]) should be(List()) + toList(new SList[Int] { + override def apply[B] = (_, f) => + f(1, + new SList[Int] { + override def apply[B] = (_, g) => g(2, nil[Int]) + } + ) + }) should be(List(1, 2)) + } + it.can("be cast from scala List") in { + fromList[Int](List())[Int](0, reduce) should be(0) + fromList[Int](List(1, 2, 3))[Int](0, reduce) should be(321) + } + + "The Either type".can("be cast to scala Either") in { + toEither(new SEither[Int, String] { + override def apply[C] = (left, _) => left(3) + }) should be(Left(3)) + + toEither(new SEither[Int, String] { + override def apply[C] = (_, right) => right("hello") + }) should be(Right("hello")) + } + it.can("be cast from scala Either") in { + fromEither[Int, String](Left(3))[String](_.toString, identity) should be("3") + fromEither[Int, String](Right("hello"))[String](_.toString, identity) should be("hello") + } + + "The tuple type".can("be cast to (,)") in { + toTuple(new STuple[Int, String] { + override def apply[C] = f => f(2, "hi") + }) should be((2, "hi")) + } + it.can("be cast from (,)") in { + fromTuple((2, "hi"))[List[String]](List.fill(_)(_)) should be(List("hi", "hi")) + fromTuple((3, 6))[Int](_ * _) should be(18) + } + + def reduce(i: Int, is: SList[Int]): Int = + i + 10 * is[Int](0, reduce) +}