1
votes

I'd like to build a Parser that can take a Map input and output a Case class. Right now I have some imperative style code that doesn't seem to take advantage of FP. I just can't see how to build a Parser that can output a Case class.

Here is what I have:

def fromMap(m: Map[String,AttributeValue]): User =  {
    val id = m.get("user-id") map {_.getS}
    val email = m.get("email").get.getS
    val name = m.get("name").get.getS
    val openId = m.get("openId") map {_.getS}
    User(id, email, name, openId)
}

UPDATE : I guess I should have been more clear that I would like to do something similar to Anorm but with the Parser Combinators in scala. The problem with the above is that is not flexible or reusable. The caller has to just take what they get.

I'm hoping that parser combinators will be the right approach for parsing results in Map form. As you might have guessed I'm working with DyanmoDB.

UPDATE 2 : Apparently I can't explain the problem very well. All I want to do is write one or more Pareser Combinators that can transform a Map to a Case class of the caller's choosing using bindings specified by the caller. Input Map => Output Case Class

UPDATE 3 : https://github.com/wfaler/scala-dynamo looks interesting but doesn't use Parser Combinators.

2
yup. I'm trying to bind dynamo results to case classes. - dres
I don't understand your update. What are your input values and what do you want to get at the end? - kiritsuku
input value is a Map[String,AttributeValue]. I want to create a Parser so that it is easy to write functions that can map that to a Case class (in this case User). - dres

2 Answers

3
votes

Similar answer to @4e6, but realized only with Scalas built-in methods:

case class User(id: String, email: String, name: String, openId: String)

val m = Map("user-id" -> "123", "email" -> "[email protected]", "name" -> "testuser", "openId" -> "456")

def fromMap(m: Map[String, String]): Option[User] = for {
  id <- m get "user-id"
  email <- m get "email"
  name <- m get "name"
  openId <- m get "openId"
} yield User(id, email, name, openId)

scala> fromMap(m)
res0: Option[User] = Some(User(123,[email protected],testuser,456))
1
votes

I think scalaz ApplicativeBuilder is what you are looking for. Also, returning an Option is preferable as a way to avoid exceptions. Don't know what getS is, so the simplified version of your snippet could be:

case class User(val id: String, val email: String, val name: String, val OpenId: String)

def fromMap(m: Map[String, String]): Option[User] = {
  val id = m.get("user-id")
  val email = m.get("email")
  val name = m.get("name")
  val openId = m.get("openId")
  id |@| email |@| name |@| openId apply User
}

To avoid confusion, apply method of ApplicationBuilder actually takes a function, so the verbose version is

(id |@| email |@| name |@| openId) { (id, email, name, openId) =>
  new User(id, email, name, openId)
}

More examples of applicatives can be found here