Operations for primitive and String singleton types
Scala
Clone or download
Latest commit e69dc50 Jul 5, 2018
Permalink
Failed to load latest commit information.
project Scala 2.13.0-M4 Jun 10, 2018
src Fixes #64 Mar 14, 2018
.gitignore Add .travis.yml May 31, 2016
.travis.yml Update to Scala 2.12.6 Jul 5, 2018
LICENSE Initial commit May 29, 2016
README.md Setting version to 0.3.0 Jul 5, 2018
build.sbt Scala 2.13.0-M4 Jun 10, 2018
version.sbt Setting version to 0.3.1-SNAPSHOT Jul 5, 2018

README.md

singleton-ops

Build Status codecov Join the chat at https://gitter.im/fthomas/singleton-ops Scaladex Scala.js

This library provides type-level operations for Typelevel Scala with SIP-23.

Simple example:

import singleton.ops._

class MyVec[L] {
  def doubleSize = new MyVec[2 * L]
  def nSize[N] = new MyVec[N * L]
  def getLength(implicit length : SafeInt[L]) : Int = length
}
object MyVec {
  implicit def apply[L](implicit check : Require[L > 0]) : MyVec[L] = new MyVec[L]()
}
val myVec : MyVec[10] = MyVec[4 + 1].doubleSize
val myBadVec = MyVec[-1] //fails compilation, as required

Using singleton-ops

The latest version of the library is 0.3.0, which is available for Typelevel Scala v4 for Scala versions 2.11.12 & 2.12.4.

If you're using sbt, add the following to your build:

libraryDependencies ++= Seq(
  "eu.timepit" %% "singleton-ops" % "0.3.0"
)

If you're using scala.js use %%% instead

libraryDependencies ++= Seq(
  "eu.timepit" %%% "singleton-ops" % "0.3.0"
)

Be sure to follow Typelevel Scala instructions, to be able to use literal types in your code.


Supported types:

  • Char with Singleton (aliased as XChar)
  • Int with Singleton (aliased as XInt)
  • Long with Singleton (aliased as XLong)
  • Float with Singleton (aliased as XFloat)
  • Double with Singleton (aliased as XDouble)
  • String with Singleton (aliased as XString)
  • Boolean with Singleton (aliased as XBoolean)
  • Nat (from Shapeless)

Supported arithmetic operations:

  • type +[P1, P2]
  • type -[P1, P2]
  • type *[P1, P2]
  • type /[P1, P2]
  • type %[P1, P2]
  • type Abs[P1]
  • type Negate[P1]

Supported relational operations:

  • type ==[P1, P2]
  • type !=[P1, P2]
  • type >[P1, P2]
  • type <[P1, P2]
  • type >=[P1, P2]
  • type <=[P1, P2]
  • type Min[P1, P2]
  • type Max[P1, P2]

Supported logical operations:

  • type &&[P1, P2]
  • type ||[P1, P2]
  • type ![P1]

Supported explicit conversion operations:

  • type ToNat[P1]
  • type ToChar[P1]
  • type ToInt[P1]
  • type ToLong[P1]
  • type ToFloat[P1]
  • type ToDouble[P1]
  • type ToString[P1]

Supported string operations:

  • type +[P1, P2] (concat)
  • type Reverse[P1]
  • type Substring[P1, P2]

Supported constraints operations:

  • type Require[P1]

Supported control operations:

  • type ==>[A, B] (first A then B)
  • type ITE[I,T,E] (If (I) Then (T) Else (E))

Supported Aux Pattern interface:

  • type OpAuxNat[O <: Op, Ret_Out <: Nat]
  • type OpAuxChar[O <: Op, Ret_Out <: XChar]
  • type OpAuxInt[O <: Op, Ret_Out <: XInt]
  • type OpAuxLong[O <: Op, Ret_Out <: XLong]
  • type OpAuxFloat[O <: Op, Ret_Out <: XFloat]
  • type OpAuxDouble[O <: Op, Ret_Out <: XDouble]
  • type OpAuxString[O <: Op, Ret_Out <: XString]
  • type OpAuxBoolean[O <: Op, Ret_Out <: XBoolean]

Examples

  • Int type operations:
import singleton.ops._
def demo[L <: XInt](implicit p : L*L + L) : p.Out {} = p.Out
val b : 30 = demo[5]
  • Long type operations:
import singleton.ops._
def demoLong[L1 <: XLong, L2 <: XLong](implicit p : Min[L1*L1, L2+L2]) : p.Out {} = p.Out
val bLong1 : 1L = demoLong[1L, 5L]
val bLong2 : 6L = demoLong[3L, 3L]
  • Double type operations:
import singleton.ops._
def demoDouble[L1 <: XDouble, L2 <: XDouble](implicit p : L1 / L2 + 1.0) : p.Out {} = p.Out
val bDouble : 1.2 = demoDouble[1.0, 5.0]
  • Combined Long and Int type operations:
import singleton.ops._
def demoSumLongInt[L1 <: XLong, L2 <: XInt](implicit p : L1 + L2) : p.Out {} = p.Out
val bSumLongInt : 16L = demoSumLongInt[8L, 8]
  • String type operations:
import singleton.ops._
def demoString[P1 <: XString](implicit op : Reverse[P1] + P1) : op.Out {} = op.Out
val bString : "cbaabc" = demoString["abc"]
  • Boolean type operations:
import singleton.ops._
def demoBoolean[P1 <: XInt](implicit op : P1 < 0) : op.Out{} = op.Out
val bBoolean1 : true = demoBoolean[-5]
val bBoolean2 : false = demoBoolean[5]
val bBoolean3 : false = demoBoolean[0]
  • Boolean type constraints:
import singleton.ops._
def demoRequire[P1 <: XInt](implicit op : Require[P1 < 0]) : op.Out{} = op.Out
scala> demoRequire[-1]
demoRequire[-1]
res0: Boolean(true) = true
scala> demoRequire[1]
<console>:16: error: could not find implicit Out for parameter op: singleton.ops.Require[singleton.ops.<[1,0]]
       demoRequire[1]
  • Shapeless' Nat type operations:
import singleton.ops._
import shapeless._
val n = Nat(5)
//Converting Nat to Int singleton occurs implicitly
def demoNatToSing[L <: Nat](implicit p : L+L) : p.Out {} = p.Out
val bSing10 : 10 = demoNatToSing[n.N]
//Converting Int singleton to Nat requires explicit `ToNat`
def demoSingToNat[L <: XInt](implicit op : ToNat[L+L]) : op.Out = op.Out
val bNat10 : shapeless.nat._10 = demoSingToNat[5]
  • Working with large numbers doesn't slay the compiler:
import singleton.ops._
def bigMul[L1 <: XLong, L2 <: XLong](implicit p : L1 * L2) : p.Out {} = p.Out
scala> bigMul[32000L, 6400000L]
res2: Long = 204800000000

Contributors and participation

The singleton-ops project supports the Typelevel code of conduct and wants all of its channels (Gitter, GitHub, etc.) to be welcoming environments for everyone.