Skip to content

Migration guide: shapeless 2.0.0 to 2.1.0

Miles Sabin edited this page Jan 18, 2015 · 8 revisions

shapeless-2.1.0 is largely backwards source compatible with shapeless-2.0.0, however you might need to make some minor changes as documented below. You might also find the complete set of differences in shapeless's examples and tests between 2.0.0 and 2.1.0 a useful guide to what's involved in updating. They can be found in this commit.

Contents

TypeClass changes

The type class deriving infrastructure included in shapeless-2.0.0 has been significantly reworked in shapeless-2.1.0. It is now implemented entierly in terms of the Generic and Lazy type class primitives. Some applications which required more flexibility that TypeClass could provide can now be implemented directly in terms of those primitives and general Scala implicit-based type level computation as seen throughout shapeless.

Applications already using the shapeless-2.0.0 TypeClass infrastructure will need the following minor modifications,

  • Removal of auto._ imports.

Previously type class instances were only derived automatically in scopes where a wildcard import from the auto member of the type classes companion object. This mechanism didn't transfer over well to the new implementation and in any case experience showed it to be overly verbose while adding little value.

  • Implicit TypeClass member replace by typeClass object in type class companion.

Where previously the type class derivation rules where introduced via an implicit definition of the form,

object Show {
  implicit val showInstance: ProductTypeClass[Show] = new LabelledTypeClass[Show] {
    def emptyProduct = ...
    def product ...

    def emptyCoproduct = ...
    def coproduct ...

    def project ...
  }
}

they are now introduced via a nested object named typeClass,

object Show {
  object typeClass extends LabelledTypeClass[Show] {
    def emptyProduct = ...
    def product ...

    def emptyCoproduct = ...
    def coproduct ...

    def project ...
  }
}

RecordType replaced by record and union type literals

In shapeless-2.0.0 record and union types were quite difficult to specify literally in source code. To make it somewhat easier a mechanism was included to allow these types to be inferred from example values,

val schema = RecordType.like('i ->> 23 :: 's ->> "foo" :: 'b ->> true :: HNil)
type R = schema.Record
type U = schema.Union

Whilst this was workable in simple case, it was verbose and clumsy.

In shapeless-2.1.0 this mechanism is replaced by an direct way to express record and union types without requiring a value for them to be inferred from,

import record._, union._
type R = Record.`'i -> Int, 's -> String, 'b -> Boolean`.T
type U = Union.`'i -> Int, 's -> String, 'b -> Boolean`.T

This is still far from ideal compared with first-class syntax for these types, nevertheless in situations where explicit types are required (primarily in tests) this notation is an improvement on what was available in 2.0.0.

No Generic instances for HList and Coproduct

In shapeless-2.0.0 Generic instances were provided for HLists and Coproducts. These instances where identity functions which, while somewhat motivated, were inconsistent with the way that other types were represented generically. Because these types are used as generic representations of other types it proved to be awkward in practice for them to have generic representations themselves, and as such it seems better to remove the instances altogether.

It is unlikely that this change will impact users of Generic. Since code expecting to handle arbitrary data types via their generic representations will already have direct handling for HLists and Coproducts the absence of Generic instances for them should be problematic and may have the benefit of eliminating sources of implicit ambiguity which might have been problematic with shapeless-2.0.0.

Record/union definitions now in labelled package

As the functionality for Coproducts and unions has been filled out to match that available for HLists and records it has become useful to factor out some common infrastructure into a shared labelled namespace. In shapeless-2.1.0 the types FieldType and FieldPoly, and the method field are now imported from shapeless.labelled.

Typeable changes for null and intersection types

In shapeless-2.0.0 it was possible to cast null to any type. This was a mistake and it shapeless-2.1.0 it is not possible to cast null at all. If you depend on the 2.0.0 behaviour you should handle null by an explicit test or lifting into Option as you would do elsewhere.

In shapeless-2.0.0 casts to types of the form A with B, A with B with C etc. would only verify the runtime type for the first element of the intersection (ie. A in the examples just given). In shapeless-2.1.0 all parts of the intersection are verified.