Nandan Rao
Nandan Rao

Reputation: 343

Map a homogenous HList of one type into a hetergenous HList of different types

I have an HList of strings:

val strings = "The Lorax" :: "Dr. Suess" :: HNil

I another HList of special types:

case class Title(title: String, words: List[String])
case class Author(firstName: String, lastName: String)
val book = Title("The Hobbit", List("Hobbit")) :: Author("J.R.R.", "Tolkien") :: HNil

I want to turn "strings", my HList of strings, into an HList of mixed types, corresponding to the "book" list. If I have a method to go from a string -> Title, and a method to go from a string -> Author, I feel like this should be very straight forward to essentially get "strings" as an instance of "book"-list-type using shapeless, but I can't seem to figure out a way.

EDIT

My use case for this involves working on HLists that started as case classes. I'm using shapeless because I want to be able to transform and modify the data of different case classes in the same way, without having to hard-code knowledge about the shape of the case classes, I only want to have to know about the types of their values. So ideally this method would work also for going from a list of strings that looks like this:

val strings2 = "Leonardo" :: "April O'Neil" :: "The Art of Pizza" :: HNil
val book2 = Author("Michaelangelo") :: Author("Donatello") :: Title("Slicing and Dicing"), List("Slicing", "Dicing") :: HList 

So I will always have an example of the format it needs to be in, but I don't want to have to hardcode the amount of "authors" and the amount of "books" into a list of translation functions. I want to be able to say "a,a,b" should look like "A, A, B", and here is a method go from "a -> A" and here is a method to go from "b -> B", but I want to be able to use the same code to go from "b, a, b" to "B, A, B", given that I have both lists.

Upvotes: 3

Views: 108

Answers (1)

Travis Brown
Travis Brown

Reputation: 139028

You can do this pretty nicely with zipApply, which applies each element of an hlist of functions to the corresponding element in another hlist:

case class Title(title: String, words: List[String])
case class Author(firstName: String, lastName: String)

// For the sake of example:
def parseTitle(s: String): Title = Title(s, s.split(' ').toList)
def parseAuthor(s: String): Author =
  Author(s.takeWhile(_ != ' '), s.dropWhile(_ != ' ').tail)

import shapeless._

val funcs = parseTitle _ :: parseAuthor _ :: HNil
val strings = "The Lorax" :: "Dr. Suess" :: HNil

val book = funcs.zipApply(strings)

And then:

scala> println(book)
Title(The Lorax,List(The, Lorax)) :: Author(Dr.,Suess) :: HNil

If you need this to be more generic, you can use the ZipApply type class instead of simply calling zipApply on hlists with concrete types.

Upvotes: 2

Related Questions