-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Abhijit Sarkar
committed
Dec 19, 2024
1 parent
a39c8b7
commit bc4c7f9
Showing
10 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package ch03 | ||
|
||
trait Bool: | ||
def `if`[A](t: A)(f: A): A | ||
|
||
val True = new Bool: | ||
def `if`[A](t: A)(f: A): A = t | ||
|
||
val False = new Bool: | ||
def `if`[A](t: A)(f: A): A = f | ||
|
||
def and(l: Bool, r: Bool): Bool = | ||
new Bool: | ||
def `if`[A](t: A)(f: A): A = | ||
l.`if`(r)(False).`if`(t)(f) | ||
|
||
def or(l: Bool, r: Bool): Bool = | ||
new Bool: | ||
def `if`[A](t: A)(f: A): A = | ||
l.`if`(True)(r).`if`(t)(f) | ||
|
||
def not(b: Bool): Bool = | ||
new Bool: | ||
def `if`[A](t: A)(f: A): A = | ||
b.`if`(True)(False).`if`(f)(t) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package ch03 | ||
|
||
/* | ||
codata is a product of destructors, where destructors are functions from | ||
the codata type (and, optionally, some other inputs) to some type. | ||
In Scala we define codata as a trait, and implement it as a final class, | ||
anonymous subclass, or an object. | ||
We have two strategies for implementing methods using codata: structural corecursion, which we can | ||
use when the result is codata, and structural recursion, which we can use when an input is codata. | ||
Data is connected to codata via fold: any data can instead be implemented as codata with | ||
a single destructor that is the fold for that data. This is called Church encoding. | ||
The reverse is also: we can enumerate all potential pairs of inputs and outputs of | ||
destructors to represent codata as data. | ||
Data and codata offer different forms of extensibility: data makes it easy to add | ||
new functions, but adding new elements requires changing existing code, while it is | ||
easy to add new elements to codata but we change existing code if we add new functions. | ||
*/ | ||
// Polymorphic function type, new in Scala 3. | ||
// https://www.youtube.com/watch?v=sauaDZ-1-zM | ||
type List[A, B] = (B, (A, B) => B) => B | ||
|
||
// Polymorphic function of two types, A and B, that takes no arg | ||
// and returns the type defined above. | ||
val Empty: [A, B] => () => List[A, B] = | ||
// empty: B | ||
[A, B] => () => (empty, _) => empty | ||
|
||
val Pair: [A, B] => (A, List[A, B]) => List[A, B] = | ||
// empty: B, f: (A, B) => B | ||
[A, B] => (head: A, tail: List[A, B]) => (empty, f) => f(head, tail(empty, f)) | ||
|
||
val list: [B] => () => List[Int, B] = | ||
[B] => () => Pair(1, Pair(2, Pair(3, Empty()))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package ch03 | ||
|
||
package data: | ||
enum List[A]: | ||
case Pair(head: A, tail: List[A]) | ||
case Empty() | ||
|
||
def foldRight[B](empty: B)(f: (A, B) => B): B = | ||
this match | ||
case Pair(head, tail) => f(head, tail.foldRight(empty)(f)) | ||
case Empty() => empty | ||
|
||
package codata: | ||
trait List[A]: | ||
def foldRight[B](empty: B)(f: (A, B) => B): B | ||
|
||
final class Pair(head: A, tail: List[A]) extends List[A]: | ||
def foldRight[B](empty: B)(f: (A, B) => B): B = | ||
f(head, tail.foldRight(empty)(f)) | ||
|
||
final class Empty extends List[A]: | ||
def foldRight[B](empty: B)(f: (A, B) => B): B = | ||
empty |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package ch03 | ||
|
||
trait Set[A]: | ||
|
||
def contains(elt: A): Boolean | ||
|
||
def insert(elt: A): Set[A] = | ||
InsertOneSet(elt, this) | ||
|
||
def union(that: Set[A]): Set[A] = | ||
UnionSet(this, that) | ||
|
||
final class ListSet[A](elements: scala.collection.immutable.List[A]) extends Set[A]: | ||
|
||
def contains(elt: A): Boolean = | ||
elements.contains(elt) | ||
|
||
override def insert(elt: A): Set[A] = | ||
ListSet(elt :: elements) | ||
|
||
override def union(that: Set[A]): Set[A] = | ||
elements.foldLeft(that) { (set, elt) => set.insert(elt) } | ||
|
||
object ListSet: | ||
def empty[A]: Set[A] = ListSet(List.empty) | ||
|
||
final class InsertOneSet[A](element: A, source: Set[A]) extends Set[A]: | ||
|
||
def contains(elt: A): Boolean = | ||
elt == element || source.contains(elt) | ||
|
||
final class UnionSet[A](first: Set[A], second: Set[A]) extends Set[A]: | ||
|
||
def contains(elt: A): Boolean = | ||
first.contains(elt) || second.contains(elt) | ||
|
||
object Evens extends Set[Int]: | ||
|
||
def contains(elt: Int): Boolean = | ||
(elt % 2 == 0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package ch03 | ||
|
||
trait Stream[A]: | ||
def head: A | ||
def tail: Stream[A] | ||
override def toString: String = head.toString | ||
|
||
def take(count: Int): scala.collection.immutable.List[A] = | ||
count match | ||
case 0 => Nil | ||
case n => head :: tail.take(n - 1) | ||
|
||
def map[B](f: A => B): Stream[B] = | ||
val self = this | ||
new Stream[B]: | ||
def head: B = f(self.head) | ||
def tail: Stream[B] = self.tail.map(f) | ||
|
||
def filter(p: A => Boolean): Stream[A] = | ||
lazy val self = if p(head) then this else tail.filter(p) | ||
new Stream[A]: | ||
def head: A = self.head | ||
def tail: Stream[A] = self.tail.filter(p) | ||
|
||
def zip[B](that: Stream[B]): Stream[(A, B)] = | ||
val self = this | ||
new Stream[(A, B)]: | ||
def head: (A, B) = (self.head, that.head) | ||
def tail: Stream[(A, B)] = self.tail.zip(that.tail) | ||
|
||
def scanLeft[B](zero: B)(f: (B, A) => B): Stream[B] = | ||
val self = this | ||
new Stream[B]: | ||
def head: B = zero | ||
def tail: Stream[B] = self.tail.scanLeft(f(zero, self.head))(f) | ||
|
||
object Stream: | ||
def unfold[A, B](seed: A)(f: A => B, next: A => A): Stream[B] = | ||
new Stream[B]: | ||
def head: B = f(seed) | ||
def tail: Stream[B] = unfold(next(seed))(f, next) | ||
|
||
val ones: Stream[Int] = | ||
new Stream[Int]: | ||
def head: Int = 1 | ||
|
||
def tail: Stream[Int] = ones | ||
|
||
val alternating: Stream[Int] = Stream.unfold(true)(if _ then 1 else -1, !_) | ||
|
||
val naturals: Stream[Int] = ones.scanLeft(1)(_ + _) | ||
|
||
val naturals2: Stream[Int] = unfold(1)(x => x, _ + 1) | ||
|
||
val naturals3: Stream[Int] = | ||
new Stream: | ||
def head = 1 | ||
def tail: Stream[Int] = naturals.map(_ + 1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package ch03 | ||
|
||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
|
||
class BoolSpec extends AnyFunSpec: | ||
describe("Bool"): | ||
it("and"): | ||
and(True, True).`if`("yes")("no") shouldBe "yes" | ||
and(True, False).`if`("yes")("no") shouldBe "no" | ||
and(False, True).`if`("yes")("no") shouldBe "no" | ||
and(False, False).`if`("yes")("no") shouldBe "no" | ||
|
||
it("or"): | ||
or(True, True).`if`("yes")("no") shouldBe "yes" | ||
or(True, False).`if`("yes")("no") shouldBe "yes" | ||
or(False, True).`if`("yes")("no") shouldBe "yes" | ||
or(False, False).`if`("yes")("no") shouldBe "no" | ||
|
||
it("not"): | ||
not(True).`if`("yes")("no") shouldBe "no" | ||
not(False).`if`("yes")("no") shouldBe "yes" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package ch03 | ||
|
||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
|
||
class CodataSpec extends AnyFunSpec: | ||
describe("Codata"): | ||
it("list"): | ||
val sum = list()(0, (a, b) => a + b) | ||
sum shouldBe 6 | ||
|
||
val product = list()(1, (a, b) => a * b) | ||
product shouldBe 6 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package ch03 | ||
|
||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
|
||
class SetSpec extends AnyFunSpec: | ||
describe("Set"): | ||
it("evens"): | ||
val evensAndOne = Evens.insert(1) | ||
val evensAndOthers = | ||
Evens.union(ListSet.empty.insert(1).insert(3)) | ||
|
||
evensAndOne.contains(1) shouldBe true | ||
evensAndOthers.contains(1) shouldBe true | ||
evensAndOne.contains(2) shouldBe true | ||
evensAndOthers.contains(2) shouldBe true | ||
evensAndOne.contains(3) shouldBe false | ||
evensAndOthers.contains(3) shouldBe true | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package ch03 | ||
|
||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
|
||
class StreamSpec extends AnyFunSpec: | ||
describe("Stream"): | ||
it("ones"): | ||
Stream.ones.take(5) shouldBe List.fill(5)(1) | ||
|
||
it("alternating"): | ||
Stream.alternating.take(5) shouldBe List.iterate(1, 5)(_ * -1) | ||
|
||
it("filter"): | ||
Stream.alternating.filter(_ > 0).take(5) shouldBe List.fill(5)(1) | ||
|
||
it("naturals"): | ||
Stream.naturals.take(5) shouldBe (1 to 5) | ||
Stream.naturals2.take(5) shouldBe (1 to 5) | ||
Stream.naturals3.take(5) shouldBe (1 to 5) | ||
|
||
|
||
|