Defining Methods on Records
Extension Methods
You can define methods on records by defining extension methods in a usual manner.
object Extension {
extension (p: % { val name: String; val age: Int }) {
def firstName: String = p.name.split(" ").head
}
}
import Extension.firstName
val person = %(name = "tarao fuguta", age = 3)
// person: % {
// val name: String
// val age: Int
// } = %(name = tarao fuguta, age = 3)
person.firstName
// res0: String = "tarao"
As you see, you need to explicitly import
the method in this case.
Extension Methods with Tags
Records can be tagged and it enables extension methods to be found automatically.
import com.github.tarao.record4s.Tag
trait Person
object Person {
extension (p: % { val name: String; val age: Int } & Tag[Person]) {
def firstName: String = p.name.split(" ").head
}
}
val person0 = %(name = "tarao fuguta", age = 3)
// person0: % {
// val name: String
// val age: Int
// } = %(name = tarao fuguta, age = 3)
val person = person0.tag[Person]
// person: % {
// val name: String
// val age: Int
// } & Tag[Person] = %(name = tarao fuguta, age = 3)
person.firstName
// res2: String = "tarao"
The mechanism is that the type of person
is an intersection type with Tag[Person]
,
which adds extension methods in object Person
to the target of implicit search at the
method invocation on person
. Without the tag, the method invocation statically fails.
person0.firstName
// error:
// value firstName is not a member of com.github.tarao.record4s.%{val name: String; val age: Int}
// error:
// Line is indented too far to the left, or a `}` is missing
// error:
// Line is indented too far to the left, or a `}` is missing
Methods on Generic Record Types
Extension methods can be generic ⸺ i.e., you can specify the least fields that are necessary for the methods.
trait Person
object Person {
extension [R <: % { val name: String }](p: R & Tag[Person]) {
def firstName: String = p.name.split(" ").head
}
}
val person = %(name = "tarao fuguta", age = 3).tag[Person]
// person: % {
// val name: String
// val age: Int
// } & Tag[Person] = %(name = tarao fuguta, age = 3)
person.firstName
// res5: String = "tarao"
val personWithEmail = person + (email = "tarao@example.com")
// personWithEmail: % {
// val name: String
// val age: Int
// val email: String
// } & Tag[Person] = %(name = tarao fuguta, age = 3, email = tarao@example.com)
personWithEmail.firstName
// res6: String = "tarao"