Skip to content

Commit

Permalink
Part of ScottEncoding task
Browse files Browse the repository at this point in the history
  • Loading branch information
sierikov committed Jan 24, 2024
1 parent 0467b49 commit 64d50d6
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 0 deletions.
86 changes: 86 additions & 0 deletions src/main/scala/sierikov/codewars/kyu1/ScottEncoding.scala
Original file line number Diff line number Diff line change
@@ -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]] = ???
}
74 changes: 74 additions & 0 deletions src/test/scala/sierikov/codewars/kyu1/ScottEncodingSpec.scala
Original file line number Diff line number Diff line change
@@ -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)
}

0 comments on commit 64d50d6

Please sign in to comment.