The purpose of this article is to prove that Scala inherits lots of the elements and syntax from FP languages such as Haskell and is not just a Java++ with a nicer lambda expressions.
In this exercise we would like to implement a simple method:
Scala
def totalPrice[P:Priceable](ps:List[P]):Double // Scala
Haskell
totalPrice :: Priceable p => [p] -> Double -- Haskell
In order to compile the method we need to give a definition for Priceable. We want Priceable to be a Typeclass, all the priceable entities must provide a method to extract the price as Double.
Scala:
trait Priceable[-A] { def extractPrice(a:A):Double }
Haskell:
class Priceable a where extractPrice :: a -> Double
Now that we have defined the Typeclass we can go back to our original method and provide the implementation:
Scala
package object exercise { def totalPrice[P:Priceable](ps:List[P]):Double = ps.map(implicitly[Priceable[P]].extractPrice).reduce(_+_) }
Haskell
totalPrice ps = foldr1 (+) (map extractPrice ps)
Finally we can create our entity Book:
Scala
case class Book(title:String, author:String, price:Double)
Haskell
data Book = Book { title::String, author::String, price::Double }
and make it member of Priceable:
Scala
object Book { implicit val bookPriceable = new Priceable[Book] { def extractPrice(book:Book):Double = book.price } }
Haskell
instance Priceable Book where extractPrice book = price book
Now lets create few books and use the priceTotal function:
Scala
val book1 = Book("The Fellowship of the Ring", "J. R. R. Tolkien", 31.45) val book2 = Book("The Da Vinci Code", "Dan Brown", 22.55) val book3 = Book("Timeline", "Michael Crichton", 21.50) val books = List(book1, book2, book3) totalPrice(books) // res0 = 75.50
Haskell
book1 = Book "The Fellowship of the Ring" "J. R. R. Tolkien" 31.45 book2 = Book "The Da Vinci Code" "Dan Brown" 22.55 book3 = Book "Timeline" "Michael Crichton" 21.50 books = [book1, book2, book3] totalPrice books -- res0 = 75.50
In Scala we can also wrap the List[P:Priceable] into an implicit class to have a more (.) oriented notation:
implicit class PriceableOp[P:Priceable](ps:List[P]) { lazy val totalPrice = exercise.totalPrice(ps) }
So now in is possible to write:
val book1 = Book("The Fellowship of the Ring", "J. R. R. Tolkien", 31.45) val book2 = Book("The Da Vinci Code", "Dan Brown", 22.55) val book3 = Book("Timeline", "Michael Crichton", 21.50) val books = List(book1, book2, book3) val result = books.totalPrice
If you want to learn more about the usage of implicit in Scala, here is a post of method extension strategy via implicit:
http://patricknoir.blogspot.com.es/2014/12/method-extension-in-scala-with-implicit.html
The article is really nice and concise. It would be better to clarify that you are using a technique named the "pimp my library pattern" in which any List having an instance of Priceable implicitly available will benefit from the totalPrice method, not any List.
ReplyDeleteHi Tom good point, I was focusing the article more on the syntax similarity between the 2 languages but I think you are right and is beneficial for the reader to know more about patterns available in scala, for this reason I will paste here the link to Martin Odersky post on "pimp my library pattern", thanks for the comment ;-) : http://www.artima.com/weblogs/viewpost.jsp?thread=179766
DeleteThanks to my friend Andy who immediately spotted my mistake on 'JJ Tolkien' name :-(, sorry for all the LOTR funs, this has now been fixed :-)
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteGreat article, Patrick! I am going to share this!
ReplyDelete