This problem is pretty hard to explain. I try to implement a kind of Arrow, call it MyArr, which extends the Arrow trait from scalaz. However, MyArr is also a kind of Algbraic Data Type, so I make a few case classes out of if, and wrote down code like this(minimal example):
package org.timeless
import scalaz._
import Scalaz._
import std.option._, std.list._
sealed trait MyArr[A,B] extends Arrow[MyArr] {
def id[A]: MyArr[A,A] = SId()
def compose[A,B,C](s1: MyArr[B,C], s2: MyArr[A,B]): MyArr[A,C] =
SRight(s2, s1)
def arr[A,B](f: A=>B): MyArr[A,B] = SPure(f)
def first[A,B,C](s: MyArr[A,B]): MyArr[(A,C),(B,C)] = SPar(s, SId())
}
case class SId[A] () extends MyArr[A,A]()
case class SRight[M[_],A,B,C](s1: MyArr[A,B], s2: MyArr[B,C]) extends MyArr[A,C]
case class SPure[A,B](f: A=>B) extends MyArr[A,B]
case class SPar[M[_],A,B,C,D](s1: MyArr[A,B], s2: MyArr[C,D]) extends MyArr[(A,C),(B,D)]
object MyArr {
val b = SId[Int]()
val c = SId[Int]()
// val a: MyArr[Int,Int] = id[Int] // Error
// val d = c compose b // Error
// val d = b >>> c // Error
val d = b >>> (b,c) // ??? Arguments
}
As easily seen, the id function is actually the constructor of SId, etc. The classes themselves compile fine, but I don't see any way to actually use it as an Arrow. I actually came from Haskell programming, so I might be doing it totally wrong, but equivalent code in Haskell should be like this(with GADTs):
data MyArr a b where
SId :: MyArr a b
SRight :: MyArr a b -> MyArr b c -> MyArr a c
SPure :: (a -> b) -> MyArr a b
SPar :: MyArr a b -> MyArr c d -> MyArr (a,c) (b,d)
instance Category MyArr where
id = SId
(.) = SRight
instance Arrow MyArr where
arr = SPure
first s = SPar s SId
Typeclasses in Scala
Typeclasses in Scala are not built into the language like in Haskell. They are just a design pattern and are usually implemented with the help of implicit arguments:
def pure[M[_], T](
implicit monad: Monad[M] // Fetching a monad instance for type M
): M[T] =
monad.pure[T] // Using methods of the monad instance
You don't have to extend the typeclass trait to provide a typeclass instance for some ADT. The typeclass instance has to be defined somewhere as an implicit val, implicit def, implicit object, etc. Then you can import the relevant instance into scope before using it.
There is also a way to supply the typeclass instance automatically, without having to import it explicitly. To do that the instance should be declared in the companion object of either the typeclass class/trait or ADT class/trait, or one of their parent classes/traits; or in a package object in which one of those is defined. You can read more on the topic of implicit resolution in this answer: Where does Scala look for implicits?
Defining a typeclass instance
So to get back to your problem, you can define your ADT like this:
sealed trait MyArr[A,B]
case class SId[A] () extends MyArr[A,A]()
case class SRight[M[_],A,B,C](s1: MyArr[A,B], s2: MyArr[B,C]) extends MyArr[A,C]
case class SPure[A,B](f: A=>B) extends MyArr[A,B]
case class SPar[M[_],A,B,C,D](s1: MyArr[A,B], s2: MyArr[C,D]) extends MyArr[(A,C),(B,D)]
And provide the Arrow instance in the companion object of MyArr:
object MyArr {
implicit val arrow: Arrow[MyArr] = new Arrow[MyArr] {
def id[A]: MyArr[A,A] = SId()
def compose[A,B,C](s1: MyArr[B,C], s2: MyArr[A,B]): MyArr[A,C] =
SRight(s2, s1)
def arr[A,B](f: A=>B): MyArr[A,B] = SPure(f)
def first[A,B,C](s: MyArr[A,B]): MyArr[(A,C),(B,C)] = SPar(s, SId())
}
def apply[T]: MyArr[T, T] = arrow.id[T]
}
Using the typeclass instance
I believe it's not possible to provide a generic id function (or return, pure, etc.) in Scala due to some limitations of Scala's type inference.
Instead you can use the MyArr.apply method defined above:
val a: MyArr[Int,Int] = MyArr[Int]
As the ADT doesn't extend the typeclass, it doesn't inherit its methods like compose or >>>. They can be provided separately using the 'Pimp My Library' pattern.
Indeed, the >>> method in scalaz comes from the ComposeOps class, which is defined like this:
final class ComposeOps[F[_, _],A, B] private[syntax]
(self: F[A, B])
(val F: Compose[F])
extends Ops[F[A, B]]
And there is an implicit conversion defined from some type F[_, _] to this class ComposeOps, if there as a Compose instance available for F:
implicit def ToComposeOps[F[_, _],A, B](v: F[A, B])(implicit F0: Compose[F])
This conversion is imported with import Scalaz._, and that's why this line works now:
val d = b >>> c
But there is no extension in scalaz that provides a compose method on ADTs with an Arrow instance, so this line still gives an error:
val e = c compose b
To make it work, you can provide an extension yourself. A simple example could look like this:
// implicit class is equivalent to a normal class
// with an implicit conversion to this class
implicit class ArrowOps[T[_, _], A, B](f: T[A, B])(implicit arrow: Arrow[T]) {
def compose[C](g: T[C, A]): T[C, B] = arrow.compose(f, g)
}
Related
I am reading a Spark in Action book, I came across a construct that even after a while working with Scala I still can't understand. Here is the code (complete code) :
First we have a case class where RDD will be converted to DataFrame using this case class:
import java.sql.Timestamp
case class Post(
commentCount:Option[Int],
lastActivityDate:Option[java.sql.Timestamp],
ownerUserId:Option[Long],
body:String,
score:Option[Int],
... )
Then we define an object with implicit class
object StringImplicits {
implicit class StringImprovements(val s: String) {
import scala.util.control.Exception.catching
def toIntSafe = catching(classOf[NumberFormatException]) opt s.toInt
def toLongSafe = catching(classOf[NumberFormatException]) opt s.toLong
def toTimestampSafe = catching(classOf[IllegalArgumentException]) opt
Timestamp.valueOf(s)
}
}
Then we import the object which is already in scope for some reason and now looks like all String object has the three methods defined in StringImprovements
import StringImplicits._
def stringToPost(row: String): Post = {
val r = row.split("~")
Post(r(0).toIntSafe,
r(1).toTimestampSafe,
r(2).toLongSafe,
r(3),
...
}
Questions:
Why do we need to need to import an already in scope object?
Which statement exactly assigned these methods to the String object, this is so confusing?
Reading implicit classes doc it makes some sense but not everything is clear. For example how come this is a valid expression
scala> 5 times println("HI")
I understand if it was
IntWithTimes(5).time(println("HI"))
Implicits are sought among all methods and values that are visible in the current scope AND within companion objects of types that appear in the implicit type you are looking for.
implicit val a: A = ???
implicit val bc: B[C] = ???
// code here sees a and bc in implicit scope
def helper = {
implicit val c: C = ???
// code here sees a and bc and c in implicit scope
}
def anotherHelper = {
val a: A = ??? // overshades a
// code here sees b in implicit scope
}
object Helper {
implicit val e: E = ???
// code here sees a, bc and e in implicit scope
}
// implicits within helper, anotherHelper and Helper are not seen here
// though Helper one's can be imported with import Helper._
class D[T]
object {
implicit def provide[T](implicit a: T): D[T] = ???
}
class E
object E {
implicit val e: E = ???
}
class F
object F
// sees no implicits that are always available but
// if you require implicitly[E], E companion will be searched
// and implicit e will be found
// if you require implicitly[D[F]], both D and F companions
// will be searched, D.provide[F] will require implicit F,
// and no such implicit will be found - failure
If you asked for implicit A[B, C, (D, E), F[G]], then Scala would look into companions of A, B, C, D, E, Tuple2, F, G (if they exist). If this rule doesn't apply (your implicits aren't in companion, your type doesn't appear in sought implicit) you have to import them manually.
When Scala sees that you try to call a method on object that doesn't exists, it will try to convert it to some object which has this method. If you have some X type, it will look for implicits your X and returning some Y (implicit defs(x: X): Y and implicit val f: X => Y) and checking if that Y has this method.
Implicit class is basically a combination of a class and implicit method:
// implicit class Y(x: X) is the same as
class Y(x: X)
implicit def convert(x: X): Y = new Y(x)
which means that you can add some implicit conversions/classes from type X into X's companion and they will be always available.
In docs:
implicit class IntWithTimes(x: Int)
created implicit conversion from Int => IntWithTimes. String doesn't have times method, but there is a conversion Int => IntWithTimes in implicit scope imperted with import Helper._ (without it compilation would fail). Its result, IntWithTimes has the times method with the right signature, so the conversion is used.
In Haskell I got:
data Foo a where
Bar :: a -> Foo a
Map :: (a -> b) -> Foo a -> Foo b
instance Functor Foo where
fmap = Map
In Scala I came up with:
import cats.Functor
trait Foo[A]
case class Bar[A](t: A) extends Foo[A]
case class Map[A,B](fun: A => B,foo: Foo[A]) extends Foo[B]
implicit val fooFunctor: Functor[Foo] = new Functor[Foo] {
def map[A,B](fa: Foo[A])(f: A => B) = Map(f,fa)
}
But Bar(40).map(_+2) gives me:
error: value map is not a member of Bar[Int]
I fairly new to Scala and don't know the way of inheritance that well.
What am I missing?
You need to upcast Bar(40) to Foo explicitly, and import the syntax implicits:
import cats.syntax.functor._
(Bar(40): Foo[Int]).map(_+2)
You need the upcast because Scala will infer the type Bar[Int] for the expression Bar(40) and this then interferes with finding the appropriate implicit that adds the map method. For this reason you sometimes see helper methods in the companion object to do the upcast for you:
object Foo {
def bar[A](a: A): Foo[A] = Bar(a)
// etc.
}
I thought a little bit about how one could solve this problem. Basically, we want to provide the functor operations even if only a super type is a functor. It seems to be impossible to express, that some higher-kinded type is the super type of an other, e.g. something like this is not possible: def bla[F[_], R[_] <: F]
We can however provide a conversion from R[] to F[] implicitly together with the functor for B:
abstract class ProvidesFor[R,F[_],FF[_[_]],A](val value: FF[F]) {
def convert(r: R): F[A]
}
implicit def providesFunctorForBar[A] =
new ProvidesFor[Bar[A],Foo,Functor,A](Functor[Foo]) {
override def convert(r: Bar[A]): Foo[A] = r
}
So the implict val will provide us with a functor for Foo and a convertion from Bar to Foo.
Now we can provide an implicit converstion to Functor.Ops like this:
implicit class FunctorOps[R[_],F[_],A](target: R[A])(implicit tc: ProvidesFor[R[A],F,Functor,A])
extends Functor.Ops[F,A] {
override val self = tc.convert(target)
override val typeClassInstance = tc.value
override def map[B](f: A => B): F[B] = typeClassInstance.map(self)(f)
override def imap[B](f: A => B)(g: B => A): F[B] = map(f)
}
Now this works as expected:
Bar(1).imap(_+2)(_+5)
We can also do the same for Map:
implicit def providesFunctorForMap[A,B] =
new ProvidesFor[Map[A,B],Foo,Functor,B](Functor[Foo]) {
override def convert(r: Map[A,B]): Foo[B] = r
}
Map((_:Int) + 1,Bar(5)).map(_+2)
For some strange reason I had to implement map and imap when extending Functor.Ops even though these methods are not abstract. In fact it compiles fine when I do not implement them but it fails at runtime with an AbstractMethodError. So somehow the compiler thinks the implementations are there but they are not. I suspect they are using some kind of byte code optimization tool that removes those implementations.
I have the simplified situation:
abstract sealed trait Top
class A[T] extends Top
class B[T] extends Top
class Typeclass[T]
implicit def a[T] = new Typeclass[A[T]]
implicit def b[T] = new Typeclass[B[T]]
Now I have a Map[String, Top] and want to use an operation on all values in the map that require the presence of an instance of Typeclass to be available in the context. This will not compile as the concrete types of the values in the map are not visible from its type and I can therefore not set a context bound for them.
Is there a way to tell the compiler that in fact there will always be an instance available? In this example this is given as there are implicit functions to generate those instances for every concrete subtype of Top.
Or is the only solution to use a HList and recurse over its type requiring all the instances to be in context?
I recommend using some variation on this adaptation of Oleg's Existentials as universals in this sort of situation ... pack the the type class instance along with the value it's the instance for,
abstract sealed trait Top
class A[T] extends Top
class B[T] extends Top
class Typeclass[T]
implicit def a[T] = new Typeclass[A[T]]
implicit def b[T] = new Typeclass[B[T]]
trait Pack {
type T <: Top
val top: T
implicit val tc: Typeclass[T]
}
object Pack {
def apply[T0 <: Top](t0: T0)(implicit tc0: Typeclass[T0]): Pack =
new Pack { type T = T0 ; val top = t0 ; val tc = tc0 }
}
val m = Map("a" -> Pack(new A[Int]), "b" -> Pack(new B[Double]))
def foo[T: Typeclass](t: T): Unit = ()
def bar(m: Map[String, Pack], k: String): Unit =
m.get(k).map { pack =>
import pack._ // imports T, top and implicit tc
foo(top) // instance available for call of foo
}
bar(m, "a")
As discussed in comment it would be more convenient to have the typeclass defined on Top, and it might be done with pattern matching.
supposing part of the definition of the typeclass is
def f[T](t: T): FResult[T],
and you have the corresponding implentations
def fOnA[T](t: A[T]): FResult[A[T]] = ...
def fOnB[T](t: B[T]): FResult[B[T]] = ...
Then you can define
def fOnT(t: Top) : FResult[Top] = t match {
case a: A[_] => fOnA(a)
// provided an FResult[A[T]] is an FResult[Top],
// or some conversion is possible
case b: B[_] => fOnB(b)
}
If is both legal and safe to call a generic method, such as fOnA[T] with an existential (a matching A[_])
However, it might be difficult to convince the compiler that the parameter you pass to f or the result you get are ok, given the reduced information of the existential. If so, please post the signatures you need.
I have (for lack of a better term) a factory method that encapsulates constructing an object:
def createMyObject = new SomeClass(a, b, c, d)
Now, depending on the context, I will need to mix in one or more traits into SomeClass:
new SomeClass with Mixin1
or
new SomeClass with Mixin2 with Mixin3
Instead of creating multiple separate factory methods for each "type" of instantiation, how can I pass in the traits to be mixed in so that it can be done with a single method? Or perhaps there is a good pattern for this that is structured differently?
I'd like to maintain the encapsulation so I'd rather not have each consumer just create the class on its own.
If you need only mixins without method overriding, you can just use type classes:
trait Marker
class C[+T <: Marker] { def b = 1 }
trait Marker1 extends Marker
implicit class I1[T <: Marker1](c: C[T]) {def a = 6 + c.b}
trait Marker2 extends Marker
implicit class I2[T <: Marker2](c: C[T]) {def a = 5 + c.b}
trait Marker3 extends Marker
implicit class I3[T <: Marker3](c: C[T]) {def k = 100}
trait Marker4 extends Marker3
implicit class I4[T <: Marker4](c: C[T]) {def z = c.k + 100} //marker3's `k` is visible here
scala> def create[T <: Marker] = new C[T]
create: [T <: Marker]=> C[T]
scala> val o = create[Marker1 with Marker3]
o: C[Marker1 with Marker3] = C#51607207
scala> o.a
res56: Int = 7
scala> o.k
res57: Int = 100
scala> create[Marker4].z
res85: Int = 200
But it won't work for create[Marker1 with Marker2].a (ambiguous implicits), so no linearization here. But if you want to just mix-in some methods (like in javascript's prototypes) and maybe inject something - seems to be fine. You can also combine it with traditional linearized mix-in by adding some traits to C, I1, I2, etc.
You can instantiate the class differently depending on the context.
def createMyObject =
if (context.foo)
new SomeClass
else
new SomeClass with Mixin1
However, if the consumers are the ones that know the traits that are supposed to be mixed in, then why wouldn't you just instantiate things there?
I have a Scala collection that contains objects of different subtypes.
abstract class Base
class A extends Base
class B extends Base
val a1 = new A()
val a2 = new A()
val b = new B()
val s = List(a1, a2, b)
I'd like to filter out all the A objects or the B objects. I can do this easily if I know the object I want to filter on at compile time.
s.filter(_.isInstanceOf[A]) // Give me all the As
s.filter(_.isInstanceOf[B]) // Give me all the Bs
Can I do it if I only know the object type to filter on at runtime? I want to write a function like this.
def filterType(xs:List[Base], t) = xs.filter(_.isInstanceOf[t])
Where t indicates whether I want objects of type A or B.
Of course I can't actually write it this way because of type erasure. Is there an idiomatic Scala way to work around this using type tags? I've been reading the Scala type tag documentation and relevant StackOverflow posts, but I can't figure it out.
This has come up a few times. Duplicate, anyone?
scala> trait Base
defined trait Base
scala> case class A(i: Int) extends Base
defined class A
scala> case class B(i: Int) extends Base
defined class B
scala> val vs = List(A(1), B(2), A(3))
vs: List[Product with Serializable with Base] = List(A(1), B(2), A(3))
scala> def f[T: reflect.ClassTag](vs: List[Base]) = vs collect { case x: T => x }
f: [T](vs: List[Base])(implicit evidence$1: scala.reflect.ClassTag[T])List[T]
scala> f[A](vs)
res0: List[A] = List(A(1), A(3))
Type erasure will destroy any information in type parameters, but objects still know what class they belong to. Because of this, we cannot filter on arbitrary types, but we can filter by class or interface/trait. ClassTag is preferable to TypeTag here.
import scala.reflect.ClassTag
def filterType[T: ClassTag](xs: List[Base]) = xs.collect {
case x: T => x
}
Which we can use like:
scala> filterType[B](s)
res29: List[B] = List(B#42096939)
scala> filterType[Base](s)
res30: List[Base] = List(A#8dbc09c, A#625f8cc7, B#42096939)
This method is safe at run-time if type T is not generic. If there was a class C[T] extends Base we could not safely filter on C[String].