title | layout |
---|---|
Meow |
main |
Catless Category Theory in Scala 3, meow ~
libraryDependencies += "us.oyanglul" %% "meow" % "0.4.+"
The most frustrating thing about using Cats is always such complaints from my teammates:
Q: What's missing? What should I import?
A: Oh, if you are trying to use <+>, you should import
cats.syntax.semigroupk._
A: Or, if you don't mind it slowing down you IDE you can just
import cats.syntax.all._
Q: But it doesn't work, the compiler is not happy and I can't make sense of it.
A: Oh, you are trying to combineK a List, you need
import cats.instances.list._
as well, since blah blah blah.
A: Or, if you don't mind you IDE get even slower, just
import cats.implicits._
.
This is extremely not user friendly, even though it makes perfect sense for lib maintainer to know that <+>
should be in SemigroupK.
But as a user, why should I care where <+>
is in the package? I should be able to guess that this Option[_]
and other Option[_]
can be glued together with <+>
, I don't need to remember if it is Semigroup, Semigroupal, SemigroupK or Alternative.
Why can't it be just like Haskell, all I need to know is that I can <|>
two Maybe
, I can >>=
or <$>
Maybe
without worrying anything about what to import.
Meow is a completely new design to be user friendly, not cat friendly, by using the latest Scala 3 features.
Meow provides idiomatic haskell-like typeclasses by using functions over methods, contextual binding over hierarchy.
All you have to do is import prelude.{given, _}
same as importing Prelude
in Haskell.
Don't worry, it won't slow down the compiler I promise, no weird class hierarchy and unnecessary syntax lookup.
import meow.prelude.{given, *}
val fa = pure[Option](1)
val fb: Option[Int] = pure(2)
val f = (x: Int) => (y: Int) => x + y
assertEquals(f `<$>` fa <*> fb, Option(3))
With the super powerful of Scala 3 inline
and Tuple
, we no longer need shapeless to auto derive typeclasses
instance for data type.
case class Inner[A](a: A)
case class Outer[A](a: A, b: Inner[A], c: List[A], d: Option[String]) derives Functor
test("derive functor") {
assertEquals(
map[Outer]((a: Int) => a + 1)(Outer(1, Inner(2), List(3,4), Option("hehe"))),
Outer(2, Inner(3), List(4,5), Option("hehe")))
}
Simply just derives Functor
and your data type will be mappable over A
.
You may want to refresh your Scala 3 knowledge before jump into the source code.
sbt test