Why Scala Records with Structural Types and Macros?

Vojin Jovanovic edited this page Dec 3, 2015 · 1 revision
Clone this wiki locally

Ideally, record types in Scala would satisfy the following requirements:

  • Fields should be named
  • Fields should be accessed with a path (e.g., person.country.state)
  • Auto-completion in the IDEs should work
  • Records should report actionable error messages
  • Least upper bounds (LUBs) of records should not depend on the order of their fields
  • Record types should be printed in a comprehensible manner
  • Records programs should compile and execute quickly

Design of Scala Records

Scala Records are represented by structural types in which every method is a macro. Structural types are printed nicely, auto-completion works, and they are accessed with a path. Macros in the method implementation take care of specialization and presenting actionable error messages. Although, they satisfy most of the above mentioned requirements, their drawback is that all operations on records are calls to whitebox macros.

Why not shapeless Records?

The first thing that came to mind, with respect to the requirements, were the shapeless records that are based on HLists. We evaluated them and found the following:

  • In benchmarks, compilation time grows exponentially with the number of fields: Compilation time of shapeless records

  • Auto-completion does not work as field information is stored in the types

  • Types of are not printed nicely: i) they are not printed in an infix position, ii) they are printed with prefixes. Here is an example:

scala> ("name" ->> "Hannah") :: ("age" ->> 30) :: HNil
res0: shapeless.::[String with shapeless.labelled.KeyTag[String("name"),String],
shapeless.::[Int with shapeless.labelled.KeyTag[String("age"),Int],
shapeless.HNil]] = Hannah :: 30 :: HNil
  • Least upper bounds of HMaps do not work because they use an HList as the underlying type and HLists impose an ordering of the fields. For example:
scala> :paste
List(
  ("name" ->> "Hannah") :: ("age" ->> 30) :: HNil, 
  ("age" ->> 30) :: ("name" ->> "Hannah") :: HNil
).head.get("name")

<console>:32: error: No field String("name") in record 
shapeless.::[shapeless.labelled.KeyTag[_ >: String("age") with String("name") <: String, _ >: Int with String],
shapeless.::[shapeless.labelled.KeyTag[_ >: String("name") with String("age") <: String, _ >: String with Int],
shapeless.HNil]]
                ).head.get("name")
                      ^                                                     

Towards a Solution That is Type-Based and Satisfies all the Requirements

We are not very proud of using structural types and macros to achieve our goals. Therefore, we are looking for a solution that would satisfy all the requirements for records, and does not require the use of structural types and macros. Until such solution is found Scala Records should serve their purpose to simplify access to structured data and to flesh-out users' requirements.