Skip to content

Instantly share code, notes, and snippets.

@dcastro
Last active July 13, 2021 20:01
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dcastro/ff2ecdf14acf58d5231fe46590000796 to your computer and use it in GitHub Desktop.
Save dcastro/ff2ecdf14acf58d5231fe46590000796 to your computer and use it in GitHub Desktop.
Shapeless - get a field's name in a type-safe way
import shapeless._
import shapeless.ops.record.Keys
import shapeless.ops.hlist.Selector
import shapeless.tag._
/**
* Gets the name of a case class's field, in a type-safe way.
* If the class does not have a field with the given name, the program won't compile.
*
* Kinda similar to C#'s `nameof` operator, but not bolted onto the language
* https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/nameof
*
* @example
*
* {{{
* > import shapeless.Witness
*
* > case class C(a: Int)
*
* > getFieldName[C, Witness.`"a"`.T].get
* res0: String = "a"
*
* > getFieldName[C, Witness.`"b"`.T].get
* error: could not find implicit (...)
* }}}
*
* Using Typelevel's scala compiler (https://typelevel.org/scala/)
*
* {{{
* > getFieldName[C, "a"].get
* res0: String = "a"
* }}}
*/
def getFieldName[A, F]: GetFieldName[A, F] = new GetFieldName
class GetFieldName[A, F] {
def get[L <: HList, KeyList <: HList](implicit
// we need to be able to convert our case class `A` to a labelled hlist `L`
ev: LabelledGeneric.Aux[A, L],
// we grab the keys of the labelled `L`, into an hlist `KeyList`
ev2: Keys.Aux[L, KeyList],
// from the key list, we select the first field with a name matching `F`
ev3: Selector[KeyList, Symbol with Tagged[F]]
): String = {
val keys: KeyList = Keys[ev.Repr].apply()
keys.select[Symbol with Tagged[F]].name
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment