learning Scalaz: day 1
Hey there. There's an updated html5 book version, if you want.
How many programming languages have been called Lisp in sheep's clothing? Java brought in GC to familiar C++ like grammar. Although there have been other languages with GC, in 1996 it felt like a big deal because it promised to become a viable alternative to C++. Eventually, people got used to not having to manage memory by hand. JavaScript and Ruby both have been called Lisp in sheep's clothing for their first-class functions and block syntax. The homoiconic nature of S-expression still makes Lisp-like languages interesting as it fits well to macros.
Recently languages are borrowing concepts from newer breed of functional languages. Type inference and pattern matching I am guessing goes back to ML. Eventually people will come to expect these features too. Given that Lisp came out in 1958 and ML in 1973, it seems to take decades for good ideas to catch on. For those cold decades, these languages were probably considered heretical or worse "not serious."
Looking back to our Scala community, it pains me to see people jeering at Scalaz. I'm not saying it's going to be the next big thing. I don't even know about it yet. But one thing for sure is that guys using it are serious about solving their problems. Or just as pedantic as the rest of the Scala community using pattern matching. Given that Haskell came out in 1990, the witch hunt may last a while, but I am going to keep an open mind.
typeclasses 101
Learn You a Haskell for Great Good says:
A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes.
Scalaz says:
It provides purely functional data structures to complement those from the Scala standard library. It defines a set of foundational type classes (e.g.
Functor,Monad) and corresponding instances for a large number of data structures.
Let's see if I can learn Scalaz by learning me a Haskell.
sbt
Here's build.sbt to test Scalaz 7. It's a slide from Nekoharu sensei's talk with some updates:
scalaVersion := "2.10.0-M7" resolvers += "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/" libraryDependencies ++= Seq( "org.scalaz" % "scalaz-core" % "7.0.0-M3" cross CrossVersion.full ) scalacOptions += "-feature" initialCommands in console := "import scalaz._, Scalaz._"
All you have to do now is open the REPL using sbt 0.12.0:
$ sbt console ... [info] downloading http://repo1.maven.org/maven2/org/scalaz/scalaz-core_2.10.0-M7/7.0.0-M3/... ... import scalaz._ import Scalaz._ Welcome to Scala version 2.10.0-M7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_33). Type in expressions to have them evaluated. Type :help for more information. scala>
There's also API docs generated for Scalaz 7.0.0 M1 by him.
Equal
LYAHFGG:
Eqis used for types that support equality testing. The functions its members implement are==and/=.
Scalaz equivalent for the Eq typeclass is called Equal:
scala> 1 === 1
res0: Boolean = true
scala> 1 === "foo"
<console>:14: error: could not find implicit value for parameter F0: scalaz.Equal[Object]
1 === "foo"
^
scala> 1 == "foo"
<console>:14: warning: comparing values of types Int and String using `==' will always yield false
1 == "foo"
^
res2: Boolean = false
scala> 1.some =/= 2.some
res3: Boolean = true
scala> 1 assert_=== 2
java.lang.RuntimeException: 1 ≠ 2Instead of the standard ==, Equal enables ===, =/=, and assert_=== syntax by declaring equal method. The main difference is that === would fail compilation if you tried to compare Int and String.
Note: I originally had /== instead of =/=, but Eiríkr Åsheim (@d6) pointed out to me:
you should encourage people to use =/= and not /== since the latter has bad precedence.
Normally comparison operators like != have lower higher precedence than &&, all letters, etc. Due to special precedence rule /== is recognized as an assignment operator because it ends with = and does not start with =, which drops to the bottom of the precedence:
scala> 1 != 2 && false res4: Boolean = false scala> 1 /== 2 && false <console>:14: error: value && is not a member of Int 1 /== 2 && false ^ scala> 1 =/= 2 && false res6: Boolean = false
Order
LYAHFGG:
Ordis for types that have an ordering.Ordcovers all the standard comparing functions such as>,<,>=and<=.
Scalaz equivalent for the Ord typeclass is Order:
scala> 1 > 2.0
res8: Boolean = false
scala> 1 gt 2.0
<console>:14: error: could not find implicit value for parameter F0: scalaz.Order[Any]
1 gt 2.0
^
scala> 1.0 ?|? 2.0
res10: scalaz.Ordering = LT
scala> 1.0 max 2.0
res11: Double = 2.0Order enables ?|? syntax which returns Ordering: LT, GT, and EQ. It also enables lt, gt, lte, gte, min, and max operators by declaring order method. Similar to Equal, comparing Int and Doubl fails compilation.
Show
LYAHFGG:
Members of
Showcan be presented as strings.
Scalaz equivalent for the Show typeclass is Show:
scala> 3.show res14: scalaz.Cord = 3 scala> 3.shows res15: String = 3 scala> "hello".println "hello"
Cord apparently is a purely functional data structure for potentially long Strings.
Read
LYAHFGG:
Readis sort of the opposite typeclass ofShow. Thereadfunction takes a string and returns a type which is a member ofRead.
I could not find Scalaz equivalent for this typeclass.
Enum
LYAHFGG:
Enummembers are sequentially ordered types — they can be enumerated. The main advantage of theEnumtypeclass is that we can use its types in list ranges. They also have defined successors and predecesors, which you can get with thesuccandpredfunctions.
Scalaz equivalent for the Enum typeclass is Enum:
scala> 'a' to 'e' res30: scala.collection.immutable.NumericRange.Inclusive[Char] = NumericRange(a, b, c, d, e) scala> 'a' |-> 'e' res31: List[Char] = List(a, b, c, d, e) scala> 3 |=> 5 res32: scalaz.EphemeralStream[Int] = scalaz.EphemeralStreamFunctions$$anon$4@6a61c7b6 scala> 'B'.succ res33: Char = C
Instead of the standard to, Enum enables |-> that returns a List by declaring pred and succ method on top of Order typeclass. There are a bunch of other operations it enables like -+-, ---, from, fromStep, pred, predx, succ, succx, |-->, |->, |==>, and |=>. It seems like these are all about stepping forward or backward, and returning ranges.
Bounded
Boundedmembers have an upper and a lower bound.
Scalaz equivalent for Bounded seems to be Enum as well.
scala> implicitly[Enum[Char]].min
res43: Option[Char] = Some(?)
scala> implicitly[Enum[Char]].max
res44: Option[Char] = Some()
scala> implicitly[Enum[Double]].max
res45: Option[Double] = Some(1.7976931348623157E308)
scala> implicitly[Enum[Int]].min
res46: Option[Int] = Some(-2147483648)
scala> implicitly[Enum[(Boolean, Int, Char)]].max
<console>:14: error: could not find implicit value for parameter e: scalaz.Enum[(Boolean, Int, Char)]
implicitly[Enum[(Boolean, Int, Char)]].max
^Enum typeclass instance returns Opton[T] for max values.
Num
Numis a numeric typeclass. Its members have the property of being able to act like numbers.
I did not find Scalaz equivalent for Num, Floating, and Integral.
typeclasses 102
I am now going to skip over to Chapter 8 Making Our Own Types and Typeclasses (Chapter 7 if you have the book) since the chapters in between are mostly about Haskell syntax.
A traffic light data type
data TrafficLight = Red | Yellow | Green
In Scala this would be:
scala> :paste // Entering paste mode (ctrl-D to finish) sealed trait TrafficLight case object Red extends TrafficLight case object Yellow extends TrafficLight case object Green extends TrafficLight
Now let's define an instance for Equal.
scala> implicit val TrafficLightEqual: Equal[TrafficLight] = Equal.equal(_ == _) TrafficLightEqual: scalaz.Equal[TrafficLight] = scalaz.Equal$$anon$7@2457733b
Can I use it?
scala> Red === Yellow <console>:18: error: could not find implicit value for parameter F0: scalaz.Equal[Product with Serializable with TrafficLight] Red === Yellow
So apparently Equal[TrafficLight] doesn't get picked up because Equal has nonvariant subtyping: Equal[F]. If I turned TrafficLight to a case class then Red and Yellow would have the same type, but then I lose the tight pattern matching from sealed #fail.
scala> :paste // Entering paste mode (ctrl-D to finish) case class TrafficLight(name: String) val red = TrafficLight("red") val yellow = TrafficLight("yellow") val green = TrafficLight("green") implicit val TrafficLightEqual: Equal[TrafficLight] = Equal.equal(_ == _) red === yellow // Exiting paste mode, now interpreting. defined class TrafficLight red: TrafficLight = TrafficLight(red) yellow: TrafficLight = TrafficLight(yellow) green: TrafficLight = TrafficLight(green) TrafficLightEqual: scalaz.Equal[TrafficLight] = scalaz.Equal$$anon$7@42988fee res3: Boolean = false
a Yes-No typeclass
Let's see if we can make our own truthy value typeclass in the style of Scalaz. Except I am going to add my twist to it for the naming convention. Scalaz calls three or four different things using the name of the typeclass like Show, show, and show, which is a bit confusing.
I like to prefix the typeclass name with Can borrowing from CanBuildFrom, and name its method as verb + s, borrowing from sjson/sbinary. Since yesno doesn't make much sense, let's call ours truthy. Eventual goal is to get 1.truthy to return true. The downside is that the extra s gets appended if we want to use typeclass instances as functions like CanTruthy[Int].truthys(1).
scala> :paste // Entering paste mode (ctrl-D to finish) trait CanTruthy[A] { self => /** @return true, if `a` is truthy. */ def truthys(a: A): Boolean } object CanTruthy { def apply[A](implicit ev: CanTruthy[A]): CanTruthy[A] = ev def truthys[A](f: A => Boolean): CanTruthy[A] = new CanTruthy[A] { def truthys(a: A): Boolean = f(a) } } trait CanTruthyOps[A] { def self: A implicit def F: CanTruthy[A] final def truthy: Boolean = F.truthys(self) } object ToCanIsTruthyOps { implicit def toCanIsTruthyOps[A](v: A)(implicit ev: CanTruthy[A]) = new CanTruthyOps[A] { def self = v implicit def F: CanTruthy[A] = ev } } // Exiting paste mode, now interpreting. defined trait CanTruthy defined module CanTruthy defined trait CanTruthyOps defined module ToCanIsTruthyOps scala> import ToCanIsTruthyOps._ import ToCanIsTruthyOps._
Here's how we can define typeclass instances for Int:
scala> implicit val intCanTruthy: CanTruthy[Int] = CanTruthy.truthys({ case 0 => false case _ => true }) intCanTruthy: CanTruthy[Int] = CanTruthy$$anon$1@71780051 scala> 10.truthy res6: Boolean = true
Next is for List[A]:
scala> implicit def listCanTruthy[A]: CanTruthy[List[A]] = CanTruthy.truthys({ case Nil => false case _ => true }) listCanTruthy: [A]=> CanTruthy[List[A]] scala> List("foo").truthy res7: Boolean = true scala> Nil.truthy <console>:23: error: could not find implicit value for parameter ev: CanTruthy[scala.collection.immutable.Nil.type] Nil.truthy
It looks like we need to treat Nil specially because of the nonvariance.
scala> implicit val nilCanTruthy: CanTruthy[scala.collection.immutable.Nil.type] = CanTruthy.truthys(_ => false) nilCanTruthy: CanTruthy[collection.immutable.Nil.type] = CanTruthy$$anon$1@1e5f0fd7 scala> Nil.truthy res8: Boolean = false
And for Boolean using identity:
scala> implicit val booleanCanTruthy: CanTruthy[Boolean] = CanTruthy.truthys(identity) booleanCanTruthy: CanTruthy[Boolean] = CanTruthy$$anon$1@334b4cb scala> false.truthy res11: Boolean = false
Using CanTruthy typeclass, let's define truthyIf like LYAHFGG:
Now let's make a function that mimics the
ifstatement, but that works withYesNovalues.
To delay the evaluation of the passed arguments, we can use pass-by-name:
scala> :paste // Entering paste mode (ctrl-D to finish) def truthyIf[A: CanTruthy, B, C](cond: A)(ifyes: => B)(ifno: => C) = if (cond.truthy) ifyes else ifno // Exiting paste mode, now interpreting. truthyIf: [A, B, C](cond: A)(ifyes: => B)(ifno: => C)(implicit evidence$1: CanTruthy[A])Any
Here's how we can use it:
scala> truthyIf (Nil) {"YEAH!"} {"NO!"} res12: Any = NO! scala> truthyIf (2 :: 3 :: 4 :: Nil) {"YEAH!"} {"NO!"} res13: Any = YEAH! scala> truthyIf (true) {"YEAH!"} {"NO!"} res14: Any = YEAH!
We'll pick it from here later.
- Login to post comments
