Search code examples
scalascalazshapeless

Coproduct with multiple values


I´m playing with Coproduct of shapeless but I dont know if I´m using wrong but I dont know how can I create a coProduct with mutlple values

Having this code

 case class Name(value: String)

  case class Age(value: Int)

  case class Sex(value: String)

  type Person = Name :+: Age :+: Sex :+: CNil

  @Test
  def main(): Unit = {
    val person = Coproduct[Person](Name("Paul"))
    println(person.select[Name])
    println(person.select[Age])
    println(person.select[Sex])
  }

How can I creare Person with Name, Age and Sex using Coproduct?.

Regards.


Solution

  • Coproduct and product are two dual constructs rooted in category theory, but in programming terms can be simplified to:

    • product includes all of the given types
    • coproduct materializes one of the given types

    For example, a Person that consists of name, age and sex should be modeleld using a product type. This is because a person consists of name, age and sex; all those types are needed in order to construct a person.

    Example of a coproduct could be anything that has a subtyping relationship, for example a Fruit can be either an Apple, a Banana or an Orange. We also use coproducts often for result types that might fail, e.g. a Response can either be a Success or a Failure. Either and Future are two examples of coproducts.

    You mixed the things up a bit, and you modelled a coproduct (perfectly correct, from the language syntax point of view) when you really needed a product. If you run your code, you will see

    Some(Name(Paul))
    None
    None
    

    This is because your Person, the way you coded it, is either a name, age or sex. Only one of those. And you then created an instance of Person by creating a name, which is fine, so if you print those three selections you will see that the name selector gives you Some(Name(Paul)), while other two are empty.

    If you code the Person as a product (in shapeless implemented via heterogenous lists), you will get:

    case class Name(value: String)
    case class Age(value: Int)
    case class Sex(value: String)
    
    type Person = HNil
    
    val person = Name("Paul") :: Age(32) :: Sex("Yes please") :: HNil
    
    println(person) // Name(Paul) :: Age(32) :: Sex(Yes please) :: HNil
    

    In plain Scala, we can model coproducts using constructs such as Either or via subtyping hierarchies (e.g. trait Fruit is extended by case classes Apple, Banana etc.), while products are usually modelled simply via case classes (in your case it would be case class Person(name: String, age: Int, sex: String).

    In shapeless, coproducts can basically be viewed as an "Either with any number of possibilities rather than just two", while product is implemented as a heterogenous list, meaning "a collection of any number of values which can also be of varying types".

    Read more about products (aka "heterogenous lists") here and about coproducts (aka "discriminated unions") here.