jamborta
jamborta

Reputation: 5210

Type classes: maintain value at type level

I would like to create a type level representation of an Int value stored something like this:

sealed trait Position {
  def value : Int
}

class P1 extends Position {val value = 1}
class P2 extends Position {val value = 2} 

And when a class extends it:

case class PExtended() extends P1

I would like to retrieve the value at type level:

def getValue[A <: Position] = ???
getValue[PExtended] //should return 1

I have tried a few things, but not sure if this is the best way to solve this problem. Any suggestion how to get started would be great.

Upvotes: 3

Views: 133

Answers (2)

Mikel San Vicente
Mikel San Vicente

Reputation: 3863

You need to implement a type class and use Shapeless Nat numbers to solve the problem:

import shapeless.ops.nat.ToInt

import shapeless.Nat
import shapeless.ops.nat.ToInt

trait Number[A] {
  def number: Int
}

object Number {

  def apply[A](implicit number: Number[A]): Number[A] = number
  implicit def positionNumber[A, N <: Nat](implicit eq: A <:< Position[N], toInt: ToInt[N]): Number[A] = {
    new Number[A] {
      override def number: Int = toInt.apply()
    }
  }
}

Then you can use it to implement your method

sealed trait Position[T <: Nat]

class P1 extends Position[Nat._1]
class P2 extends Position[Nat._2]

object Test extends App {
  case class PExtended() extends P1

  def getValue[A](implicit number: Number[A]): Int = {
    number.number
  }

  println(getValue[PExtended])
}

Upvotes: 1

Dmytro Mitin
Dmytro Mitin

Reputation: 51658

You can either add value of type A

def getValue[A <: Position](a: A): Int = a.value
println(getValue(new PExtended))//1

or make value a type

type One
type Two

sealed trait Position1 {
  type Value
}

class P11 extends Position1 { override type Value = One }
class P21 extends Position1 { override type Value = Two }

implicitly[P11#Value =:= P11#Value]//compiles
implicitly[P11#Value =:= P21#Value]//doesn't compile

Or you can use implicits

implicit val p1: P1 = new P1
implicit val p2: P2 = new P2

def getValue[A <: Position](implicit a: A): Int = a.value

println(getValue[P1])//1
println(getValue[P2])//2

I agree with @YuvalItzchakov that you can look at shapeless.Nat

https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/nat.scala#L29-L49

For example

import shapeless.Nat
import shapeless.ops.nat.{Sum, ToInt}
import shapeless.Nat._

implicitly[Sum.Aux[_2, _3, _5]]//compiles

or

def calculateSum[N <: Nat, M <: Nat,
                 NplusM <: Nat](n: N, m: M)(implicit
  sum: Sum.Aux[N, M, NplusM],
  toInt: ToInt[NplusM]): Int = toInt()

println(calculateSum(_2, _3)) // 5

Upvotes: 1

Related Questions