2

Summoning an instance by means of a method context parameter allows to customize the error message declaratively annotating the parameter with @implicitNotFound which requires a literal string that supports a type placeholder.

import scala.annotation.implicitNotFound
        
inline def singleInhabitantOf[A](using @implicitNotFound("The received type ${A} is not a singleton") voA: ValueOf[A]): A =
   voA.value
    
singleInhabitantOf[Option[Int]] // Compile error: The received type Option[Int] is not a singleton

If instead the summoning is implemented with summonFrom, we can customize the error message imperatively calling the error method which also requires a literal string but has no type placeholder support.

import scala.compiletime.{summonFrom, error}
        
inline def singleInhabitantOf2[A]: A = summonFrom {
    case voA: ValueOf[A] => voA.value
    case _ => error("The received type ${A} is not a singleton")    
}

singleInhabitantOf2[Option[Int]] // compiler error: The received type ${A} is not a singleton

Is there a way, other than using macros, to summon an implicit instance in the body (like summonFrom does) that has type placeholder support or, much better, dynamic string interpolation?

1 Answer 1

2

Many standard inline methods (error, summonFrom, summonInline, constValue etc.) are actually implemented as macros or compiler internals.

For example you seem to have nothing against using error. It's implemented in compiler internals. Suppose it wouldn't be implemented there. Then we'd have to use our own macro

import scala.quoted.{Quotes, Expr, quotes}

inline def error(inline msg: String): Nothing = ${errorImpl('msg)}
def errorImpl(msg: Expr[String])(using Quotes): Expr[Nothing] =
  import quotes.reflect.*
  report.errorAndAbort(msg.valueOrAbort)

There is shapeless3.typeable.Typeable[A].describe but it's not inline, so can't be used in error.

summonFrom { case m: Mirror.Of[A] => error("The received type " + constValue[m.MirroredLabel] + " is not a singleton") } can be used for sealed traits and case classes but not arbitrary A.

I can't find this in standard library (although it's worth to be there) but you can define your own small macro and use it everywhere in ordinary inline methods

// different file
import scala.quoted.{Quotes, Type, Expr}

inline def typeOf[A]: String = ${typeOfImpl[A]}
def typeOfImpl[A: Type](using Quotes): Expr[String] = Expr(Type.show[A])
import scala.compiletime.{summonFrom, error}

// ordinary inline method, not macro
inline def singleInhabitantOf2[A]: A = summonFrom {
  case voA: ValueOf[A] => voA.value
  case _ => error("The received type " + typeOf[A] + " is not a singleton")
}

error argument can't be written as s"The received type ${typeOf[A]} is not a singleton" because interpolated string is not a string literal.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.