Качественная программа — (1) достигает (2) требуемого результата (3) с соблюдением заданных ограничений и (4) минимизирует целевую функцию.
1
, ""
A & B
, A | B
A => B
Context ?=> A => B
[A] => List[A] => B
[X, Y] =>> Map[Y, X]
(лямбды в пространстве типов)
type Fibonacci[a <: Int, b <: Int, n <: Int] <: Int = n match
case 0 => b
case S[x] => Fibonacci[b, a + b, x]
реализация
transparent inline def fibonacci[n <: Int](inline a: Int, inline b: Int): Int =
inline erasedValue[n] match
case _: 0 => b
case _: S[x] =>
fibonacci[x](b, a + b)
(8 ферзей)
Тесты?
def f[A](a: A): A = ???
val f: [A] => A => A = ???
val identity: [A] => A => A = [A] => (a: A) => a
map
, flatMap
, foldLeft
, traverse
, ...Тесты?
Тесты?
Не тестируемые проблемы
Тесты?
val x: String | Null = null // ok
-Yexplicit-nulls
Тесты?
def foo(a: A|Null, b: B|Null): Int = (a, b) match
case (null, null) => 0
case (null, _) => 1
case ( _, null) => 2
case _ => 3
def bar(a: A, b: B): Int = 3
type PortNumber = Int Refined And[Not[Less[0]], Less[65536]]
val a: String Refined NonEmpty = refineMV[NonEmpty]("a")
Тесты?
Дополнительные возможности
def slice(n: Int Refined Positive): [A] => List[A] => List[List[A]] =
[a] => (lst: List[a]) =>
val (init, tail) = lst.splitAt(n)
if tail.isEmpty then
List(init)
else
init :: slice(n)(tail)
// http://nikita-volkov.github.io/refined/
slice :: Refined Positive Int -> [a] -> [[a]]
slice n l =
splitAt (unrefine n) l & \(a, b) -> a : slice n b
n≤0
Тесты?
Тесты?
в библиотеке:
sealed trait LogReceipt // почти `Unit`
def log(message: String): IO[LogReceipt] =
IO{...}// побочный эффект — сохранение сообщения во внешнюю систему
.as(new LogReceipt{})
Тесты?
sealed trait SavedToDBReceipt[A]
def saveToDB[A](a: A): IO[SavedToDBReceipt[A]] =
IO{...}// побочный эффект — сохранение в БД
.as(new SavedToDBReceipt[A]{})
opaque type DBGeneratedID[A] = Long
def saveToDB[A](a: A): IO[DBGeneratedID[A]] =
IO{...}// побочный эффект — сохранение в БД с возвратом ID
.map(id => id : DBGeneratedID[A])
Тесты?
*:
Тесты?
def fewSteps(): IO[Set3[LogReceipt, SavedToDBReceipt[Person], SavedToDBReceipt[Product]]] =
for
logReceipt <- log("")
savedPerson <- saveToDB(person)
savedProduct <- saveToDB(product)
yield
Set(logReceipt, savedPerson, savedProduct)
def handle(): IO[Unit] =
for
receipts <- fewSteps()
_ = setEquals[Set3[
SavedToDBReceipt[Product],
LogReceipt,
SavedToDBReceipt[Person]
]](receipts)
_ = setIsASuperset[Set1[LogReceipt]]
yeild
Http.SuccessOk
trait Monoid[T]:
def empty: T
def combine(t1: T, t2: T): T
class Monoid t where
mempty :: t
mappend :: t -> t -> t
final
, библиотечных)
given tupleMonoid[A, B <: Tuple](using ma: Monoid[A], mb: Monoid[B]): Monoid[A *: B] =
new Monoid[A *: B]:
def empty: A *: B = ma.empty *: mb.empty
def combine(t1: A *: B, t2: A *: B): A *: B =
ma.combine(t1.head, t2.head) *: mb.combine(t1.tail, t2.tail)
вклад в качество
case class Person1(name: String)
case class Person2(id: Int, name: String)
def convert(p2: Person2): Person1 = Person1(p2.name)
def convert(id: Int, p1: Person1): Person2 = Person2(id, p1.name)
abstract final class Person
object personProps extends RecordSchemaBuilder[Person]:
val id = property[Int]("id")
val name = property[String]("name")
val Schema1 = fields(name)
val Schema2 = fields(id, name)
val bs1 = personProps.Schema1
val bs2 = personProps.Schema2
val person1: bs1.Values = bs1.values(("Vasya"))
val person2: bs2.Values = bs2.values((20, "Vasya"))
val lst1 = List(person2).projection(personProps.Schema1)
Тесты?
Спасибо за внимание
Арсений Александрович Жижелев, Праймтолк / zhizhelev@primetalk.ru