Skip to content

Commit e41cd40

Browse files
committed
Some new tests
- Some variants of typeclasses - Revised Pouring.scala with some namign improvements
1 parent e2c9dc0 commit e41cd40

File tree

3 files changed

+223
-26
lines changed

3 files changed

+223
-26
lines changed

Diff for: tests/pos/typeclasses.scala

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
class Common:
2+
3+
// this should go in Predef
4+
infix type at [A <: { type This}, B] = A { type This = B }
5+
6+
trait Ord:
7+
type This
8+
extension (x: This)
9+
def compareTo(y: This): Int
10+
def < (y: This): Boolean = compareTo(y) < 0
11+
def > (y: This): Boolean = compareTo(y) > 0
12+
13+
trait SemiGroup:
14+
type This
15+
extension (x: This) def combine(y: This): This
16+
17+
trait Monoid extends SemiGroup:
18+
def unit: This
19+
20+
trait Functor:
21+
type This[A]
22+
extension [A](x: This[A]) def map[B](f: A => B): This[B]
23+
24+
trait Monad extends Functor:
25+
def pure[A](x: A): This[A]
26+
extension [A](x: This[A])
27+
def flatMap[B](f: A => This[B]): This[B]
28+
def map[B](f: A => B) = x.flatMap(f `andThen` pure)
29+
end Common
30+
31+
32+
object Instances extends Common:
33+
34+
/*
35+
instance Int: Ord as intOrd with
36+
extension (x: Int)
37+
def compareTo(y: Int) =
38+
if x < y then -1
39+
else if x > y then +1
40+
else 0
41+
*/
42+
given intOrd: Ord with
43+
type This = Int
44+
extension (x: Int)
45+
def compareTo(y: Int) =
46+
if x < y then -1
47+
else if x > y then +1
48+
else 0
49+
/*
50+
instance List[T: Ord]: Ord as listOrd with
51+
extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys) match
52+
case (Nil, Nil) => 0
53+
case (Nil, _) => -1
54+
case (_, Nil) => +1
55+
case (x :: xs1, y :: ys1) =>
56+
val fst = x.compareTo(y)
57+
if (fst != 0) fst else xs1.compareTo(ys1)
58+
*/
59+
60+
// Proposed short syntax:
61+
// given listOrd[T: Ord as ord]: Ord at T with
62+
given listOrd[T](using ord: Ord { type This = T}): Ord with
63+
type This = List[T]
64+
extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys) match
65+
case (Nil, Nil) => 0
66+
case (Nil, _) => -1
67+
case (_, Nil) => +1
68+
case (x :: xs1, y :: ys1) =>
69+
val fst = x.compareTo(y)
70+
if (fst != 0) fst else xs1.compareTo(ys1)
71+
end listOrd
72+
73+
/*
74+
instance List: Monad as listMonad with
75+
extension [A](xs: List[A]) def flatMap[B](f: A => List[B]): List[B] =
76+
xs.flatMap(f)
77+
def pure[A](x: A): List[A] =
78+
List(x)
79+
*/
80+
81+
given listMonad: Monad with
82+
type This[A] = List[A]
83+
extension [A](xs: List[A]) def flatMap[B](f: A => List[B]): List[B] =
84+
xs.flatMap(f)
85+
def pure[A](x: A): List[A] =
86+
List(x)
87+
88+
/*
89+
type Reader[Ctx] = X =>> Ctx => X
90+
instance Reader[Ctx: _]: Monad as readerMonad with
91+
extension [A](r: Ctx => A) def flatMap[B](f: A => Ctx => B): Ctx => B =
92+
ctx => f(r(ctx))(ctx)
93+
def pure[A](x: A): Ctx => A =
94+
ctx => x
95+
*/
96+
97+
given readerMonad[Ctx]: Monad with
98+
type This[X] = Ctx => X
99+
extension [A](r: Ctx => A) def flatMap[B](f: A => Ctx => B): Ctx => B =
100+
ctx => f(r(ctx))(ctx)
101+
def pure[A](x: A): Ctx => A =
102+
ctx => x
103+
104+
extension (xs: Seq[String])
105+
def longestStrings: Seq[String] =
106+
val maxLength = xs.map(_.length).max
107+
xs.filter(_.length == maxLength)
108+
109+
extension [T](xs: List[T])
110+
def second = xs.tail.head
111+
def third = xs.tail.tail.head
112+
113+
//Proposed short syntax:
114+
//extension [M: Monad as m, A](xss: M[M[A]])
115+
// def flatten: M[A] =
116+
// xs.flatMap(identity)
117+
118+
extension [M, A](using m: Monad)(xss: m.This[m.This[A]])
119+
def flatten: m.This[A] =
120+
xss.flatMap(identity)
121+
122+
// Proposed short syntax:
123+
//def maximum[T: Ord](xs: List[T]: T =
124+
def maximum[T](xs: List[T])(using Ord at T): T =
125+
xs.reduceLeft((x, y) => if (x < y) y else x)
126+
127+
// Proposed short syntax:
128+
// def descending[T: Ord as asc]: Ord at T = new Ord:
129+
def descending[T](using asc: Ord at T): Ord at T = new Ord:
130+
type This = T
131+
extension (x: T) def compareTo(y: T) = asc.compareTo(y)(x)
132+
133+
// Proposed short syntax:
134+
// def minimum[T: Ord](xs: List[T]) =
135+
def minimum[T](xs: List[T])(using Ord at T) =
136+
maximum(xs)(using descending)
137+
138+
def test(): Unit =
139+
val xs = List(1, 2, 3)
140+
println(maximum(xs))
141+
println(maximum(xs)(using descending))
142+
println(maximum(xs)(using descending(using intOrd)))
143+
println(minimum(xs))
144+
145+
// Adapted from the Rust by Example book: https://doc.rust-lang.org/rust-by-example/trait.html
146+
//
147+
// lines words chars
148+
// wc Scala: 30 115 853
149+
// wc Rust : 57 193 1466
150+
trait Animal:
151+
type This
152+
// Associated function signature; `This` refers to the implementor type.
153+
def apply(name: String): This
154+
155+
// Method signatures; these will return a string.
156+
extension (self: This)
157+
def name: String
158+
def noise: String
159+
def talk(): Unit = println(s"$name, $noise")
160+
end Animal
161+
162+
class Sheep(val name: String):
163+
var isNaked = false
164+
def shear() =
165+
if isNaked then
166+
println(s"$name is already naked...")
167+
else
168+
println(s"$name gets a haircut!")
169+
isNaked = true
170+
171+
/*
172+
instance Sheep: Animal with
173+
def apply(name: String) = Sheep(name)
174+
extension (self: This)
175+
def name: String = self.name
176+
def noise: String = if self.isNaked then "baaaaah?" else "baaaaah!"
177+
override def talk(): Unit =
178+
println(s"$name pauses briefly... $noise")
179+
*/
180+
181+
// Implement the `Animal` trait for `Sheep`.
182+
given Animal with
183+
type This = Sheep
184+
def apply(name: String) = Sheep(name)
185+
extension (self: This)
186+
def name: String = self.name
187+
def noise: String = if self.isNaked then "baaaaah?" else "baaaaah!"
188+
override def talk(): Unit =
189+
println(s"$name pauses briefly... $noise")
190+
191+
/*
192+
193+
- In a type pattern, A <: T, A >: T, A: T, A: _ are all allowed and mean
194+
T is a fresh type variable (T can start with a capital letter).
195+
- instance definitions
196+
- `as m` syntax in context bounds and instance definitions
197+
198+
*/

Diff for: tests/run/Pouring.check

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
Vector(Empty(0), Empty(1), Fill(0), Fill(1), Pour(0,1), Pour(1,0))
2-
Fill(1) Pour(1,0) Empty(0) Pour(1,0) Fill(1) Pour(1,0) --> Vector(4, 6)
1+
Illegal command line: more arguments expected

Diff for: tests/run/Pouring.scala

+24-24
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,35 @@
1-
class Pouring(capacity: Vector[Int]):
2-
type Glass = Int
3-
type Content = Vector[Int]
1+
type Glass = Int
2+
type Levels = Vector[Int]
43

5-
enum Move:
6-
def apply(content: Content): Content = this match
7-
case Empty(g) => content.updated(g, 0)
8-
case Fill(g) => content.updated(g, capacity(g))
9-
case Pour(from, to) =>
10-
val amount = content(from) min (capacity(to) - content(to))
11-
extension (s: Content) def adjust(g: Glass, delta: Int) = s.updated(g, s(g) + delta)
12-
content.adjust(from, -amount).adjust(to, amount)
4+
class Pouring(capacity: Levels):
135

6+
enum Move:
147
case Empty(glass: Glass)
158
case Fill(glass: Glass)
169
case Pour(from: Glass, to: Glass)
10+
11+
def apply(levels: Levels): Levels = this match
12+
case Empty(glass) =>
13+
levels.updated(glass, 0)
14+
case Fill(glass) =>
15+
levels.updated(glass, capacity(glass))
16+
case Pour(from, to) =>
17+
val amount = levels(from) min (capacity(to) - levels(to))
18+
levels.updated(from, levels(from) - amount)
19+
.updated(to, levels(to) + amount)
1720
end Move
1821

22+
val glasses = 0 until capacity.length
1923
val moves =
20-
val glasses = 0 until capacity.length
21-
22-
(for g <- glasses yield Move.Empty(g))
24+
(for g <- glasses yield Move.Empty(g))
2325
++ (for g <- glasses yield Move.Fill(g))
2426
++ (for g1 <- glasses; g2 <- glasses if g1 != g2 yield Move.Pour(g1, g2))
2527

26-
class Path(history: List[Move], val endContent: Content):
28+
class Path(history: List[Move], val endContent: Levels):
2729
def extend(move: Move) = Path(move :: history, move(endContent))
2830
override def toString = s"${history.reverse.mkString(" ")} --> $endContent"
29-
end Path
30-
31-
val initialContent: Content = capacity.map(x => 0)
32-
val initialPath = Path(Nil, initialContent)
3331

34-
def from(paths: Set[Path], explored: Set[Content]): LazyList[Set[Path]] =
32+
def from(paths: Set[Path], explored: Set[Levels]): LazyList[Set[Path]] =
3533
if paths.isEmpty then LazyList.empty
3634
else
3735
val extensions =
@@ -44,14 +42,16 @@ class Pouring(capacity: Vector[Int]):
4442
paths #:: from(extensions, explored ++ extensions.map(_.endContent))
4543

4644
def solutions(target: Int): LazyList[Path] =
45+
val initialContent: Levels = capacity.map(_ => 0)
46+
val initialPath = Path(Nil, initialContent)
4747
for
4848
paths <- from(Set(initialPath), Set(initialContent))
4949
path <- paths
5050
if path.endContent.contains(target)
5151
yield path
5252
end Pouring
5353

54-
@main def Test =
55-
val problem = Pouring(Vector(4, 7))
56-
println(problem.moves)
57-
println(problem.solutions(6).head)
54+
@main def Test(target: Int, capacities: Int*) =
55+
val problem = Pouring(capacities.toVector)
56+
println(s"Moves: ${problem.moves}")
57+
println(s"Solution: ${problem.solutions(target).headOption}")

0 commit comments

Comments
 (0)