Skip to content

Instantly share code, notes, and snippets.

@malcolmgreaves
Last active February 27, 2019 22:29
Show Gist options
  • Save malcolmgreaves/2acd9a743ffe47af461a2ba2dfb13057 to your computer and use it in GitHub Desktop.
Save malcolmgreaves/2acd9a743ffe47af461a2ba2dfb13057 to your computer and use it in GitHub Desktop.
Idea for a Scala macro to make working with tagged types more ergonomic.

Main Point

Idea for a Scala macro to make working with tagged types more ergonomic.

Explanation

As a user, I want to define two variables:

  • $TYPE : The name of the new type that we are creating.
  • $CONTENT : The actual, underlying content of the new type.

This will define a new type called $TYPE that is a transparent, no-overhead wrapper around a value of type $CONTENT.

After supplying these values, I want to automatically produce the the following code:

import scalaz.@@

type $TYPE = $TYPE.Content @@ $TYPE.Phantom
object $TYPE {
  def apply(x: Content): $TYPE = Tag[Content, Phantom](x)
  def apply(x: $TYPE): Content = Tag.unwrap[Content, Phantom](x)
  sealed trait Phantom
  type Content = $CONTENT
}

Substituting $CONTENT and $TYPE for the actual, user-defined values.

This above code means that if we have a value named x of type $CONTENT, then we can transpartently, easily convert it to and fro. Suppose we want to convert x to an instance of $TYPE. We can do so with the method $TYPE.apply: we can say $TYPE(x) to do this.

Let's say we have a value named y in the context val y = $TYPE(x). We can convert y to the actual content type with the same syntax: $TYPE(y).

Maybe use the macro as:

@tagged type $TYPE = newtype[$CONTENT]

Example

Right now, to have a newtype called Average (a floating point average), I'd need to write:

import scalaz.@@

type Average = Average.Content @@ Average.Phantom
object Average {
  def apply(x: Content): Average = Tag[Content, Phantom](x)
  def apply(x: Average): Content = Tag.unwrap[Content, Phantom](x)
  sealed trait Phantom
  type Content = Double
}

This is boilerplate-y :(. Instead, I'd like to be able to do something similiar to:

@tagged type Average = newtype[Double]

Next Steps

Investigate:

  • How to make such a macro?
    • Generating the code from the 2 inputs.
    • Scala snytax for invoking such a macro.
    • Using an annotation? A method value?
@malcolmgreaves
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment