0

I've Conjuction type

type ![S] = S => Nothing
type !![S] = ![![S]]
type ∨[T, U] = ![![T] with ![U]]
type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

Class hierarchy : a,b,c,d, n

abstract class State
case class A() extends State
case class B() extends State
case class N() extends State

// what should i place in ??? , state.type doesn't compile : 
// Error: not found value state ... in evidence
case class C(state: State)(implicit ev: (A |∨| B)#λ[???]) 
case class D(state: State)(implicit ev: (A |∨| B |∨| C)#λ[???])

samples :

val a = A(); val b = B(); val n = N()
val ca = C(a); val cb = C(b)
//this shouldn't compile because evidence (A |∨| B)
val cn = C(n)

How to implement evidence in right way ??

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
HsNamed
  • 63
  • 6

1 Answers1

0

Try to parametrize case classes:

  type ![S] = S => Nothing
  type !![S] = ![![S]]
  type ∨[T, U] = ![![T] with ![U]]
  type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class N() extends State

  case class C[S <: State](state: S)(implicit ev: (A |∨| B)#λ[S])

  val a = A()
  val b = B()
  val n = N()

  val ca = C(a)
  val cb = C(b)
  // val cn = C(n) // doesn't compile

Encoding for more classes

  type ![S] = S => Nothing
  type !![S] = ![![S]]

  trait Disj[T] {
    type or[S] = Disj[T with ![S]]
    type apply = ![T]
  }

  // for convenience
  type disj[T] = { type or[S] = Disj[![T]]#or[S] }

  type w[T, U, V] = disj[T]#or[U]#or[V]#apply
  type ww[T, U, V] = {type λ[X] = !![X] <:< w[T, U, V]}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class C() extends State
  case class N() extends State

  case class D[S <: State](state: S)(implicit ev: ww[A, B, C]#λ[S])

  val a = A()
  val b = B()
  val c = C()
  val n = N()

  val da = D(a)
  val db = D(b)
  val dc = D(c)
//  val dn = D(n) // doesn't compile

Or you can use apply method

  type ![S] = S => Nothing
  type !![S] = ![![S]]
  type ∨[T, U] = ![![T] with ![U]]
  type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class N() extends State

  class C private(state: State)
  object C {
    def apply(state: State)(implicit ev: (A |∨| B)#λ[state.type]) = new C(state)
  }

  val a = A()
  val b = B()
  val n = N()

  val ca = C(a)
  val cb = C(b)
//  val cn = C(n) // doesn't compile

If you'd like to avoid companion objects with apply method (for some reason), you can consider secondary constructors

  type ![S] = S => Nothing
  type !![S] = ![![S]]
  type ∨[T, U] = ![![T] with ![U]]
  type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class N() extends State

  // "ignored" is to avoid constructor ambiguity
  class C private(state: State, ignored: Int) {
    def this(state: State)(implicit ev: (A |∨| B)#λ[state.type]) = this(state, 0)
  }

  val a = A()
  val b = B()
  val n = N()

  val ca = new C(a)
  val cb = new C(b)
//  val cn = new C(n) // doesn't compile

Using secondary constructor or companion object's apply method is a standard workaround to handle this kind of dependency in constructors (1 2 3). The alternative is to wait for real union types in Dotty (current approach is just partial emulation of them) or resolving SI-5712 in Scala.

For automatical generation of companion objects you can consider macros or code generation.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • The things are : i'm trying to escape companion objects for every class in hierarchy , making class C type parametrized requires type param in evidence definition (A |∨| B |∨| C[**T**]) - but i dont want to know what type class in C until i call it. – HsNamed Jan 17 '19 at 06:59