Kevin Meredith
Kevin Meredith

Reputation: 41909

Function to Accept Any Type but `AnyVal`?

Looking at the Akka docs for creating an Actor:

The recommended approach to create the actor Props is not supported for cases when the actor constructor takes value classes as arguments.

Is it possible to define a function:

def anyTypeButValueClass[A : ...](x: A) = ???

where A is any type except for a value class at compile-time?

My understanding is that value classes extend AnyVal,(per docs) but there's also:

The standard implementation includes nine AnyVal subtypes:

Double, Float, Long, Int, Char, Short, and Byte are the numeric value types.

Unit and Boolean are the non-numeric value types.

per AnyVal.

How can this function be written?

Upvotes: 1

Views: 1199

Answers (3)

Gabriele Petronella
Gabriele Petronella

Reputation: 108091

If you're willing to use a library like shapeless, you could also encode it like

import shapeless._

def anyTypeButValueClass[A: |¬|[AnyVal]#λ](x: A) = x

ref: https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/package.scala#L54

example:

@ def anyTypeButValueClass[A: |¬|[AnyVal]#λ](x: A) = x
defined function anyTypeButValueClass

@ anyTypeButValueClass(42)
Compilation Failed
Main.scala:296: ambiguous implicit values:
 both method nsubAmbig1 in package shapeless of type [A, B >: A]=> shapeless.<:!<[A,B]
 and method nsubAmbig2 in package shapeless of type [A, B >: A]=> shapeless.<:!<[A,B]
 match expected type shapeless.<:!<[Int,AnyVal]
anyTypeButValueClass(42)
                ^
@ anyTypeButValueClass("foo")
res0: String = "foo"

Upvotes: 1

Lee
Lee

Reputation: 144126

The Scala type hierarchy shows that Any has two direct subtypes, AnyRef and AnyVal for reference and value types respectively.

This means you can just add an AnyRef constraint:

def anyTypeButValueClass[A <: AnyRef](x: A) = ???

Upvotes: 6

Alexey Romanov
Alexey Romanov

Reputation: 170713

"Accept any type but AnyVal" is answered at Enforce type difference, but it's very different from "Accept any type which doesn't extend AnyVal", which is again different from "Accept any type except for a value class". Value classes do extend AnyVal, but so do the primitive types.

You can do it in this way:

// just a marker trait
sealed trait NotAValueClass[A]

object NotAValueClass {
  implicit def AnyRefIsNotAValueClass[A <: AnyRef]: NotAValueClass[A] = new NotAValueClass[A] {}
  implicit val IntIsNotAValueClass: NotAValueClass[Int] = new NotAValueClass[Int] {}
  ... // same for other primitive types
}

def anyTypeButValueClass[A : NotAValueClass](x: A) = ...

Upvotes: 2

Related Questions