• Stars
    star
    906
  • Rank 48,320 (Top 1.0 %)
  • Language
    Scala
  • License
    MIT License
  • Created about 12 years ago
  • Updated about 1 month ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Compiler plugin for making type lambdas (type projections) easier to write

Kind Projector

Continuous Integration Maven Central

Note on maintenance

This project is only maintained for Scala 2.x. No new features are developed, but bug fix releases will still be made available. Dotty/Scala 3 has built-in type lambda syntax and kind-projector compatible syntax.

Dedication

"But I don't want to go among mad people," Alice remarked.

"Oh, you can't help that," said the Cat: "we're all mad here. I'm mad. You're mad."

"How do you know I'm mad?" said Alice.

"You must be," said the Cat, "or you wouldn't have come here."

--Lewis Carroll, "Alice's Adventures in Wonderland"

Overview

One piece of Scala syntactic noise that often trips people up is the use of type projections to implement anonymous, partially-applied types. For example:

// partially-applied type named "IntOrA"
type IntOrA[A] = Either[Int, A]

// type projection implementing the same type anonymously (without a name).
({type L[A] = Either[Int, A]})#L

Many people have wished for a better way to do this.

The goal of this plugin is to add a syntax for type lambdas. We do this by rewriting syntactically valid programs into new programs, letting us seem to add new keywords to the language. This is achieved through a compiler plugin performing an (un-typed) tree transformation.

One problem with this approach is that it changes the meaning of (potentially) valid programs. In practice this means that you must avoid defining the following identifiers:

  1. Lambda and Ξ»
  2. *, +*, and -*
  3. Ξ›$
  4. Ξ±$, Ξ²$, ...

If you find yourself using lots of type lambdas, and you don't mind reserving those identifiers, then this compiler plugin is for you!

Using the plugin

Kind-projector supports Scala 2.11, 2.12, and 2.13.

Note: as of version 0.11.0 the plugin is published against the full Scala version (see #15)

To use this plugin in your own projects, add the following lines to your build.sbt file:

addCompilerPlugin("org.typelevel" % "kind-projector" % "0.13.2" cross CrossVersion.full)

Note: for multi-project builds - put addCompilerPlugin clause into settings section for each sub-project.

For maven projects, add the plugin to the configuration of the maven-scala-plugin (remember to use _2.11.12, _2.12.15, _2.13.6 etc as appropriate):

<plugin>
  <groupId>net.alchim31.maven</groupId>
  <artifactId>scala-maven-plugin</artifactId>
  ...
  <configuration>
    <compilerPlugins>
      <compilerPlugin>
        <groupId>org.typelevel</groupId>
        <artifactId>kind-projector_2.13.6</artifactId>
        <version>0.13.2</version>
      </compilerPlugin>
    </compilerPlugins>
  </configuration>
</plugin>

For mill projects, add the plugin to the scalacPluginIvyDep Note the triple colons (:::) to ensure it uses the full scala version.

override def scalacPluginIvyDeps = Agg(
  ivy"org.typelevel:::kind-projector:0.13.2"
)

That's it!

Versions of the plugin earlier than 0.10.0 were released under a different organization (org.spire-math).

Inline Syntax

The simplest syntax to use is the inline syntax. This syntax resembles Scala's use of underscores to define anonymous functions like _ + _.

Since underscore is used for existential types in Scala (and it is probably too late to change this syntax), we use * for the same purpose. We also use +* and -* to handle covariant and contravariant types parameters.

Here are a few examples:

Tuple2[*, Double]        // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +*]          // equivalent to: type R[+A] = Either[Int, A]
Function2[-*, Long, +*]  // equivalent to: type R[-A, +B] = Function2[A, Long, B]
EitherT[*[_], Int, *]    // equivalent to: type R[F[_], B] = EitherT[F, Int, B]

As you can see, this syntax works when each type parameter in the type lambda is only used in the body once, and in the same order. For more complex type lambda expressions, you will need to use the function syntax.

Inline Underscore Syntax

Since version 0.13.0 kind-projector adds an option to use underscore symbol _ instead of * to define anonymous type lambdas. The syntax roughly follows the proposed new syntax for wildcards and placeholders for Scala 3.2+ and is designed to allow cross-compilation of libraries between Scala 2 and Scala 3 while using the new Scala 3 syntax for both versions.

To enable this mode, add -P:kind-projector:underscore-placeholders to your scalac command-line. In sbt you may do this as follows:

ThisBuild / scalacOptions += "-P:kind-projector:underscore-placeholders"

This mode is designed to be used with scalac versions 2.12.14+ and 2.13.6+, these versions add an the ability to use ? as the existential type wildcard (scala/scala#9560), allowing to repurpose the underscore without losing the ability to write existential types. It is not advised that you use this mode with older versions of scalac or without -Xsource:3 flag, since you will lose the underscore syntax entirely.

Here are a few examples:

Tuple2[_, Double]        // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +_]          // equivalent to: type R[+A] = Either[Int, A]
Function2[-_, Long, +_]  // equivalent to: type R[-A, +B] = Function2[A, Long, B]
EitherT[_[_], Int, _]    // equivalent to: type R[F[_], B] = EitherT[F, Int, B]

Examples with -Xsource:3's ?-wildcard:

Tuple2[_, ?]        // equivalent to: type R[A] = Tuple2[A, x] forSome { type x }
Either[?, +_]          // equivalent to: type R[+A] = Either[x, A] forSome { type x }
Function2[-_, ?, +_]  // equivalent to: type R[-A, +B] = Function2[A, x, B] forSome { type x }
EitherT[_[_], ?, _]    // equivalent to: type R[F[_], B] = EitherT[F, x, B] forSome { type x }

Function Syntax

The more powerful syntax to use is the function syntax. This syntax resembles anonymous functions like x => x + 1 or (x, y) => x + y. In the case of type lambdas, we wrap the entire function type in a Lambda or Ξ» type. Both names are equivalent: the former may be easier to type or say, and the latter is less verbose.

Here are some examples:

Lambda[A => (A, A)]              // equivalent to: type R[A] = (A, A)
Lambda[(A, B) => Either[B, A]]   // equivalent to: type R[A, B] = Either[B, A]
Lambda[A => Either[A, List[A]]]  // equivalent to: type R[A] = Either[A, List[A]]

Since types like (+A, +B) => Either[A, B] are not syntactically valid, we provide two alternate methods to specify variance when using function syntax:

  • Plus/minus: (+[A], +[B]) => Either[A, B]
  • Backticks: (`+A`, `+B`) => Either[A, B]

(Note that unlike names like *, + and - do not have to be reserved. They will only be interpreted this way when used in parameters to Lambda[...] types, which should never conflict with other usage.)

Here are some examples with variance:

Ξ»[`-A` => Function1[A, Double]]          // equivalent to: type R[-A] = Function1[A, Double]
Ξ»[(-[A], +[B]) => Function2[A, Int, B]]  // equivalent to: type R[-A, +B] = Function2[A, Int, B]
Ξ»[`+A` => Either[List[A], List[A]]]      // equivalent to: type R[+A] = Either[List[A], List[A]]

The function syntax also supports higher-kinded types as type parameters. The syntax overloads the existential syntax in this case (since the type parameters to a type lambda should never contain an existential).

Here are a few examples with higher-kinded types:

Lambda[A[_] => List[A[Int]]]  // equivalent to: type R[A[_]] = List[A[Int]]
Lambda[(A, B[_]) => B[A]]     // equivalent to: type R[A, B[_]] = B[A]

Finally, variance annotations on higher-kinded sub-parameters are supported using backticks:

Lambda[`x[+_]` => Q[x, List] // equivalent to: type R[x[+_]] = Q[x, List]
Lambda[`f[-_, +_]` => B[f]   // equivalent to: type R[f[-_, +_]] = B[f]

The function syntax with backtick type parameters is the most expressive syntax kind-projector supports. The other syntaxes are easier to read at the cost of being unable to express certain (hopefully rare) type lambdas.

Type lambda gotchas

The inline syntax is the tersest and is often preferable when possible. However, there are some type lambdas which it cannot express.

For example, imagine that we have trait Functor[F[_]].

You might want to write Functor[Future[List[*]]], expecting to get something like:

type X[a] = Future[List[a]]
Functor[X]

However, * always binds at the tightest level, meaning that List[*] is interpreted as type X[a] = List[a], and that Future[List[*]] is invalid.

In these cases you should prefer the lambda syntax, which would be written as:

Functor[Lambda[a => Future[List[a]]]]

Other types which cannot be written correctly using inline syntax are:

  • Lambda[a => (a, a)] (repeated use of a).
  • Lambda[(a, b) => Either[b, a]] (reverse order of type params).
  • Lambda[(a, b) => Function1[a, Option[b]]] (similar to example).

(And of course, you can use Ξ»[...] instead of Lambda[...] in any of these expressions.)

Under The Hood

This section shows the exact code produced for a few type lambda expressions.

Either[Int, *]
({type Ξ›$[Ξ²$0$] = Either[Int, Ξ²$0$]})#Ξ›$

Function2[-*, String, +*]
({type Ξ›$[-Ξ±$0$, +Ξ³$0$] = Function2[Ξ±$0$, String, Ξ³$0$]})#Ξ›$

Lambda[A => (A, A)]
({type Ξ›$[A] = (A, A)})#Ξ›$

Lambda[(`+A`, B) => Either[A, Option[B]]]
({type Ξ›$[+A, B] = Either[A, Option[B]]})#Ξ›$

Lambda[(A, B[_]) => B[A]]
({type Ξ›$[A, B[_]] = B[A]})#Ξ›$

As you can see, names like Ξ›$ and Ξ±$ are forbidden because they might conflict with names the plugin generates.

If you dislike these unicode names, pass -Dkp:genAsciiNames=true to scalac to use munged ASCII names. This will use L_kp in place of Ξ›$, X_kp0$ in place of Ξ±$, and so on.

Polymorphic lambda values

Scala does not have built-in syntax or types for anonymous function values which are polymorphic (i.e. which can be parameterized with types). To illustrate that consider both of these methods:

def firstInt(xs: List[Int]): Option[Int] = xs.headOption
def firstGeneric[A](xs: List[A]): Option[A] = xs.headOption

Having implemented these methods, we can see that the second just generalizes the first to work with any type: the function bodies are identical. We'd like to be able to rewrite each of these methods as a function value, but we can only represent the first method (firstInt) this way:

val firstInt0: List[Int] => Option[Int] = _.headOption
val firstGeneric0 <what to put here???>

(One reason to want to do this rewrite is that we might have a method like .map which we'd like to pass an anonymous function value.)

Several libraries define their own polymorphic function types, such as the following polymorphic version of Function1 (which we can use to implement firstGeneric0):

trait PolyFunction1[-F[_], +G[_]] {
  def apply[A](fa: F[A]): G[A]
}

val firstGeneric0: PolyFunction1[List, Option] =
  new PolyFunction1[List, Option] {
    def apply[A](xs: List[A]): Option[A] = xs.headOption
  }

It's nice that PolyFunction1 enables us to express polymorphic function values, but at the level of syntax it's not clear that we've saved much over defining a polymorphic method (i.e. firstGeneric).

Since 0.9.0, Kind-projector provides a value-level rewrite to fix this issue and make polymorphic functions (and other types that share their general shape) easier to work with:

val firstGeneric0 = Ξ»[PolyFunction1[List, Option]](_.headOption)

Either Ξ» or Lambda can be used (in a value position) to trigger this rewrite. By default, the rewrite assumes that the "target method" to define is called apply (as in the previous example), but a different method can be selected via an explicit call.

In the following example we are using the polymorphic lambda syntax to define a run method on an instance of the PF trait:

trait PF[-F[_], +G[_]] {
  def run[A](fa: F[A]): G[A]
}

val f = Lambda[PF[List, Option]].run(_.headOption)

It's possible to nest this syntax. Here's an example taken from the wild of using nested polymorphic lambdas to remove boilerplate:

// without polymorphic lambdas, as in the slide
def injectFC[F[_], G[_]](implicit I: Inject[F, G]) =
  new (FreeC[F, *] ~> FreeC[G, *]) {
    def apply[A](fa: FreeC[F, A]): FreeC[G, A] =
      fa.mapSuspension[Coyoneda[G, *]](
        new (Coyoneda[F, *] ~> Coyoneda[G, *]) {
          def apply[B](fb: Coyoneda[F, B]): Coyoneda[G, B] = fb.trans(I)
        }
      )
  }

// with polymorphic lambdas
def injectFC[F[_], G[_]](implicit I: Inject[F, G]) =
  Ξ»[FreeC[F, *] ~> FreeC[G, *]](
    _.mapSuspension(Ξ»[Coyoneda[F, *] ~> Coyoneda[G, *]](_.trans(I)))
  )

Kind-projector's support for type lambdas operates at the type level (in type positions), whereas this feature operates at the value level (in value positions). To avoid reserving too many names the Ξ» and Lambda names were overloaded to do both (mirroring the relationship between types and their companion objects).

Here are some examples of expressions, along with whether the lambda symbol involved represents a type (traditional type lambda) or a value (polymorphic lambda):

// type lambda (type level)
val functor: Functor[Ξ»[a => Either[Int, a]]] = implicitly

// polymorphic lambda (value level)
val f = Ξ»[Vector ~> List](_.toList)

// type lambda (type level)
trait CF2 extends Contravariant[Ξ»[a => Function2[a, a, Double]]] {
  ...
}

// polymorphic lambda (value level)
xyz.translate(Ξ»[F ~> G](fx => fx.flatMap(g)))

One pattern you might notice is that when Ξ» occurs immediately within [] it is referring to a type lambda (since [] signals a type application), whereas when it occurs after = or within () it usually refers to a polymorphic lambda, since those tokens usually signal a value. (The () syntax for tuple and function types is an exception to this pattern.)

The bottom line is that if you could replace a Ξ»-expression with a type constructor, it's a type lambda, and if you could replace it with a value (e.g. new Xyz[...] { ... }) then it's a polymorphic lambda.

Polymorphic lambdas under the hood

What follows are the gory details of the polymorphic lambda rewrite.

Polymorphic lambdas are a syntactic transformation that occurs just after parsing (before name resolution or typechecking). Your code will be typechecked after the rewrite.

Written in its most explicit form, a polymorphic lambda looks like this:

Ξ»[Op[F, G]].someMethod(<expr>)

and is rewritten into something like this:

new Op[F, G] {
  def someMethod[A](x: F[A]): G[A] = <expr>(x)
}

(The names A and x are used for clarity –- in practice unique names will be used for both.)

This rewrite requires that the following are true:

  • F and G are unary type constructors (i.e. of shape F[_] and G[_]).
  • <expr> is an expression of type Function1[_, _].
  • Op is parameterized on two unary type constructors.
  • someMethod is parametric (for any type A it takes F[A] and returns G[A]).

For example, Op might be defined like this:

trait Op[M[_], N[_]] {
  def someMethod[A](x: M[A]): N[A]
}

The entire Ξ»-expression will be rewritten immediately after parsing (and before name resolution or typechecking). If any of these constraints are not met, then a compiler error will occur during a later phase (likely type-checking).

Here are some polymorphic lambdas along with the corresponding code after the rewrite:

val f = Lambda[NaturalTransformation[Stream, List]](_.toList)
val f = new NaturalTransformation[Stream, List] {
  def apply[A](x: Stream[A]): List[A] = x.toList
}

type Id[A] = A
val g = Ξ»[Id ~> Option].run(x => Some(x))
val g = new (Id ~> Option) {
  def run[A](x: Id[A]): Option[A] = Some(x)
}

val h = Ξ»[Either[Unit, *] Convert Option](_.fold(_ => None, a => Some(a)))
val h = new Convert[Either[Unit, *], Option] {
  def apply[A](x: Either[Unit, A]): Option[A] =
    x.fold(_ => None, a => Some(a))
}

// that last example also includes a type lambda.
// the full expansion would be:
val h = new Convert[({type Ξ›$[Ξ²$0$] = Either[Unit, Ξ²$0$]})#Ξ›$, Option] {
  def apply[A](x: ({type Ξ›$[Ξ²$0$] = Either[Unit, Ξ²$0$]})#Ξ›$): Option[A] =
    x.fold(_ => None, a => Some(a))
}

Unfortunately the type errors produced by invalid polymorphic lambdas are likely to be difficult to read. This is an unavoidable consequence of doing this transformation at the syntactic level.

Building the plugin

You can build kind-projector using SBT 0.13.0 or newer.

Here are some useful targets:

  • compile: compile the code
  • package: build the plugin jar
  • test: compile the test files (no tests run; compilation is the test)
  • console: launch a REPL with the plugin loaded so you can play around

You can use the plugin with scalac by specifying it on the command-line. For instance:

scalac -Xplugin:kind-projector_2.13.6-0.13.2.jar test.scala

Releasing the plugin

This project must use full cross-versioning and thus needs to be republished for each new release of Scala, but if the code doesn't change, we prefer not to ripple downstream with a version bump. Thus, we typically republish from a tag.

  1. Be sure you're on Java 8.

  2. Run ./scripts/back-publish with the tag and Scala version

    $ ./scripts/back-publish
    Usage: ./scripts/back-publish [-t <tag>] [-s <scala_version>]
    $ ./scripts/back-publish -t v0.13.2 -s 2.13.8

You can also run the above steps in CI by manually triggering the workflow in backpublish.yml. To do so, navigate to the Actions tab, select the Back-Publish workflow from the left side-bar, and click Run workflow to access a drop-down menu to run the workflow.

Screen Shot 2022-01-11 at 11 09 12

Known issues & errata

When dealing with type parameters that take covariant or contravariant type parameters, only the function syntax is supported. Huh???

Here's an example that highlights this issue:

def xyz[F[_[+_]]] = 12345
trait Q[A[+_], B[+_]]

// we can use kind-projector to adapt Q for xyz
xyz[Ξ»[`x[+_]` => Q[x, List]] // ok

// but these don't work (although support for the second form
// could be added in a future release).
xyz[Q[*[+_], List]]          // invalid syntax
xyz[Q[*[`+_`], List]]        // unsupported

There have been suggestions for better syntax, like [A, B]Either[B, A] or [A, B] => Either[B, A] instead of Lambda[(A, B) => Either[B, A]]. Unfortunately this would actually require modifying the parser (i.e. the language itself) which is outside the scope of this project (at least, until there is an earlier compiler phase to plug into).

Others have noted that it would be nicer to be able to use _ for types the way we do for values, so that we could use Either[Int, _] to define a type lambda the way we use 3 + _ to define a function. Unfortunately, it's probably too late to modify the meaning of _, which is why we chose to use * instead.

Future Work

As of 0.5.3, kind-projector should be able to support any type lambda that can be expressed via type projections, at least using the function syntax. If you come across a type for which kind-projector lacks a syntax, please report it.

Disclaimers

Kind projector is an unusual compiler plugin in that it runs before the typer phase. This means that the rewrites and renaming we are doing are relatively fragile, and the author disclaims all warranty or liability of any kind.

(That said, there are currently no known bugs.)

If you are using kind-projector in one of your projects, please feel free to get in touch to report problems (or a lack of problems)!

Community

People are expected to follow the Scala Code of Conduct when discussing Kind-projector on GitHub or other venues.

The project's current maintainers are:

Copyright and License

All code is available to you under the MIT license, available at http://opensource.org/licenses/mit-license.php and also in the COPYING file.

Copyright Erik Osheim, 2011-2021.

More Repositories

1

cats

Lightweight, modular, and extensible library for functional programming.
Scala
5,120
star
2

fs2

Compositional, streaming I/O library for Scala
Scala
2,319
star
3

scalacheck

Property-based testing for Scala
Scala
1,908
star
4

cats-effect

The pure asynchronous runtime for Scala
Scala
1,817
star
5

spire

Powerful new number types and numeric abstractions for Scala.
Scala
1,753
star
6

skunk

A data access library for Scala + Postgres.
Scala
1,545
star
7

simulacrum

First class syntax support for type classes in Scala
Scala
936
star
8

squants

The Scala API for Quantities, Units of Measure and Dimensional Analysis
Scala
910
star
9

frameless

Expressive types for Spark.
Scala
869
star
10

cats-collections

Data structures for pure functional programming in Scala
Scala
557
star
11

kittens

Automatic type class derivation for Cats
Scala
522
star
12

jawn

Jawn is for parsing jay-sawn (JSON)
Scala
431
star
13

log4cats

Logging Tools For Interaction with cats-effect
Scala
390
star
14

Laika

Site and E-book Generator and Customizable Text Markup Transformer for sbt, Scala and Scala.js
Scala
387
star
15

algebra

Experimental project to lay out basic algebra type classes
Scala
379
star
16

mouse

A small companion to cats
Scala
347
star
17

sbt-tpolecat

scalac options for the enlightened
Scala
328
star
18

discipline

Flexible law checking for Scala
Scala
322
star
19

natchez

functional tracing for cats
Scala
317
star
20

cats-mtl

cats transformer type classes.
Scala
304
star
21

cats-tagless

Library of utilities for tagless final encoded algebras
Scala
301
star
22

CT_from_Programmers.scala

Scala sample code for Bartosz Milewski's CT for Programmers
Scala
279
star
23

fs2-grpc

gRPC implementation for FS2/cats-effect
Scala
258
star
24

cats-parse

A parsing library for the cats ecosystem
Scala
224
star
25

machinist

Spire's macros for zero-cost operator enrichment
Scala
191
star
26

cats-effect-testing

Integration between cats-effect and test frameworks
Scala
184
star
27

paiges

an implementation of Wadler's a prettier printer
Scala
183
star
28

shapeless-3

Generic programming for Scala
Scala
168
star
29

grackle

Grackle: Functional GraphQL for the Typelevel stack
Scala
163
star
30

sbt-typelevel

Let sbt work for you.
Scala
151
star
31

feral

Feral cats are homeless, feral functions are serverless
Scala
144
star
32

munit-cats-effect

Integration library for MUnit & cats-effect
Scala
142
star
33

catbird

Birds and cats together
Scala
140
star
34

otel4s

An OpenTelemetry library for Scala based on Cats-Effect
Scala
138
star
35

fs2-chat

Sample project demonstrating use of fs2-io to build a chat client and server
Scala
123
star
36

spotted-leopards

Proof of concept for a cats-like library built using Dotty features
Scala
112
star
37

fabric

Object-Notation Abstraction for JSON, binary, HOCON, etc.
Scala
110
star
38

literally

Compile time validation of literal values built from strings
Scala
102
star
39

toolkit

Quickstart your next app with the Typelevel Toolkit!
Scala
92
star
40

cats-time

Cats Instances for Java Time
Scala
91
star
41

typelevel-nix

Development tools for Typelevel projects
Nix
87
star
42

vault

Type-safe, persistent storage for values of arbitrary types
Scala
81
star
43

shapeless-contrib

Interoperability libraries for Shapeless
Scala
79
star
44

cats-effect-cps

An incubator project for async/await syntax support for Cats Effect
Scala
78
star
45

scalacheck-effect

Effectful property testing built on ScalaCheck
Scala
76
star
46

coop

Cooperative multithreading as a pure monad transformer
Scala
68
star
47

claimant

Library to support automatic labeling of ScalaCheck properties.
Scala
68
star
48

typeclassic

Everything you need to make type classes first class.
Scala
61
star
49

scalaz-contrib

Interoperability libraries & additional data structures and instances for Scalaz
Scala
55
star
50

twiddles

Micro-library for building effectful protocols
Scala
55
star
51

monoids

Generic Monoids for Scala
Scala
51
star
52

fs2-netty

What it says on the tin!
Scala
47
star
53

sbt-catalysts

sbt utilities for open source projects
Scala
45
star
54

natchez-http4s

Glorious integration layer for Natchez and Http4s.
Scala
44
star
55

typelevel.github.com

Web site of typelevel.scala
HTML
38
star
56

jawn-fs2

Integration between jawn and fs2
Scala
36
star
57

keypool

A Keyed Pool Implementation for Scala
Scala
34
star
58

scalaz-specs2

Specs2 bindings for Scalaz
Scala
34
star
59

catalysts

Scala
34
star
60

case-insensitive

A case-insensitive string for Scala
Scala
34
star
61

simulacrum-scalafix

Simulacrum as Scalafix rules
Scala
33
star
62

scalaz-outlaws

outcasts no longer allowed in the ivory tower
Scala
28
star
63

scalac-options

A library for configuring scalac options
Scala
27
star
64

bobcats

Typelevel's very own CryptoKitties!
Scala
27
star
65

ce3.g8

Scala
24
star
66

scalaz-scalatest

Scalatest bindings for scalaz.
Scala
23
star
67

general

Repository for general Typelevel information, activity and issues
19
star
68

discipline-munit

MUnit binding for Typelevel Discipline
Scala
18
star
69

unique

Unique Functional Values for Scala
Scala
18
star
70

cats-testkit-scalatest

Cats Testkit for Scalatest
Scala
18
star
71

discipline-scalatest

ScalaTest binding for Discipline
Scala
17
star
72

typelevel-scalafix

Scalafix rules for Typelevel projects
Scala
17
star
73

semigroups

Scala
16
star
74

cats-effect-shell

Command line debugging console for Cats Effect
Scala
15
star
75

jdk-index

A Jabba compatible index of JDK versions
Scala
14
star
76

cats-uri

URI implementation based on cats-parse with cats instances
Scala
14
star
77

typelevel.g8

A typelevel.g8 based on sbt-typelevel
Scala
14
star
78

catapult

Scala
13
star
79

weaver-test

A test framework that runs everything in parallel.
Scala
11
star
80

discipline-specs2

Specs2 Integration for Discipline
Scala
8
star
81

governance

Typelevel governance
Scala
7
star
82

catz-cradle

Testbed for scala libraries and tools, based on examples from cats docs
Scala
7
star
83

spire-contrib

Interoperability libraries for spire
Shell
7
star
84

idna4s

Cross-platform Scala implementation of Internationalized Domain Names in Applications
Scala
6
star
85

scalac-compat

Lightweight tools for tackling Scalac version incompatibilities
Scala
6
star
86

steward

Runs Scala Steward for Typelevel projects
5
star
87

cats-effect-main

3
star
88

sacagawea

Common infrastructure for tracing functional effects
Scala
3
star
89

scalacheck-xml

Scalacheck instances for scala-xml
Scala
3
star
90

sorcery

WIP
2
star
91

scalacheck-web

ScalaCheck Web Site
Nix
2
star
92

sbt-catalysts.g8

Scala
2
star
93

feral.g8

Giter8 template for feral serverless
Scala
2
star
94

download-java

2
star
95

toolkit.g8

A Giter8 template for Typelevel Toolkit!
Scala
2
star
96

sbt-tls-crossproject

sbt-crossproject plugin for Typelevel Scala
Scala
1
star
97

catalysts-docker

Shell
1
star
98

await-cirrus

Depend on Cirrus CI from a GitHub Actions workflow
JavaScript
1
star
99

.github

a ✨special ✨ repository for project defaults and organization readme
1
star