I'm currently working on a project that includes Gaussian Processes for Machine Learning. Considering the examples and explanations in the book, I'm trying to create a generic function for the various parameters that are part of a trained GP-object - thus, the following declaration is the most general one for a (simple) training function.
def train[T, M <: MatrixInverter[T], S <: Kernel[T]](): GP_Spawn[T] = null
(I've removed the parameter list and the implementation, just if you're wondering.)
Tdescribes the numeric type, e.g. it may be Double or Int. MatrixInverter[T] is a trait that enforces a calculateInverse function. Kernel[T] is the corresponding trait for a kernel-function.
As some of you may already know, training in a gaussian process can be changed (somehow simplified) when using a Cholesky-Decomposition as the matrix-inverter - thus, I've considered to specialize the function mentioned above. Due to the documentation of the #specialized tag, it should be something like this:
def train[T, #specialized(CholeskyDecomposition[T]) M <: MatrixInverter[T], S <: Kernel[T]](): GP_Spawn[T]
It's obvious that all parameters are more or less depending on T, since they need to use some variables (types T,Vector[T],Matrix[T]) that depend on it. If I try to compile the code mentioned above, the scala-compiler (2.9.2) complains about
error: not found: value CholeskyDecomposition
I'm not sure what this means, since the import import algorithms.{CholeskyDecomposition, MatrixInverter} is correct. Besides, it's curious to see that the import of CholeskyDecomposition is marked as Unused import statement. CholeskyDecomposition has a companion that includes some constants that are related to the algorithm itself, but I don't assume this aspect to be the reason for this error.
Any ideas what may cause this error ? And, furthermore, how to solve it without cutting of the generic approach ?
Edit:
After reading the answers are considering some re-ordering of my code, I ended up with a solution at runtime that uses type-matching.
val testMat = new Matrix[T](3, 3)
val testInv = fac(testMat)
testInv match {
case chol : CholeskyDecomposition[T] => println("Found Cholesky!")
case _ => println("Found something different.")
}
And it works now :) Thanks to all!
You can only specialize a generic parameter with a primitive type: Int, Double, etc. So you can specialize T but not Foo[T] even if T is a primitive.
If you have a class C that is specialized on type T, then
class D[#specialized T, C[T]](c: C[T]) { ... }
will use the T-specialized verson of C.
This is all you need anyway. There is no point to specialization on object; generic code works just as well since everything non-primitive is an object anyway.
As per the API, it says:
Type T can be specialized on a subset of the primitive types by
specifying a list of primitive types to specialize at:
So it is basically only for primitive types.
Related
I'm facing issues due to two scala compiler limitations
It's not possible to refer to the type of this (this.type is not it for obv reasons) for cases where you want to write traits which implement common behavior which requires construction of a concrete type. The recommended pattern to work around this is
trait Foo[SelfType <: Foo[SelfType]] {
this: SelfType =>
final def foo: SelfType = newInstance(??? /*do some work*/)
protected def newInstance(i: Int): SelfType
}
Scala compiler (fixed in Dotty AFAIK) does not keep track of bounds in existential types (I've found at least 3 bugs filed with the scala github project going back from 2009 and on). This means, given the above type if I were to write a method that take some unknown Foo as such
def process(f: Foo[_])
I may get some weird buggy compiler behavior in certain cases. Instead, I need to manually express to the compiler that the _ here obeys the type bounds to work around its limitations. For that, I'd need to do something like
def process[F <: Foo[F]](f: Foo[F])
and invoke the method without passing any type params which hopefully captures this correctly. However, this can make def signatures quite cumbersome if Foo takes other type params leading to a lot of burden for clients. So my question is, is it possible to use type definitions as a shorthand to express such self-referential types correctly. E.g. I've tried all sorts of things.
type SomeFoo = Foo[SelfType] with SelfType forSome { type SelfType <: Foo[SelfType] }
but that doesn't seem to quite do it. Somehow the compiler doesn't realize that SelfType is the same unknown type SomeFoo.
I hope this made sense. Thanks for your help. P.S. feel free to suggest renaming the question as it's not super clear right now.
I would like to use the cats-saga from that repository: https://github.com/VladKopanev/cats-saga
However I am stuck on that piece of code at OrderSagaCoordinator.scala L160:
def apply[F[_]: Sync: Concurrent: Timer: Sleep: Parallel](
paymentServiceClient: PaymentServiceClient[F],
loyaltyPointsServiceClient: LoyaltyPointsServiceClient[F],
orderServiceClient: OrderServiceClient[F],
sagaLogDao: SagaLogDao[F],
maxRequestTimeout: Int
): F[OrderSagaCoordinatorImpl[F]] =
What is F, where does it come from, can someone explain that piece of code ?
Thanks
Edit: I know what a generic type is. However in that case the apply method is called without specifying the concrete type and I do not see any places where it came from.
(for {
paymentService <- PaymentServiceClientStub(randomUtil, clientMaxReqTimeout, flakyClient)
loyaltyPoints <- LoyaltyPointsServiceClientStub(randomUtil, clientMaxReqTimeout, flakyClient)
orderService <- OrderServiceClientStub(randomUtil, clientMaxReqTimeout, flakyClient)
xa = Transactor.fromDriverManager[IO]("org.postgresql.Driver", "jdbc:postgresql:Saga", "postgres", "root")
logDao = new SagaLogDaoImpl(xa)
orderSEC <- OrderSagaCoordinatorImpl(paymentService, loyaltyPoints, orderService, logDao, sagaMaxReqTimeout)
// ...
Think of something concrete, say 'box of chocolates'
case class Box(v: Chocolate)
Now imagine we take away the chocolate, and make the box take any kind of element A, maybe box of coins, box of candy, box of cards, etc
case class Box[A](v: A)
Here we are polymorphic in the element type of the box. Many languages can express this level of polymorphism. But Scala takes this further. In the same way how we took away the chocolate, we can take away the box itself, essentially expressing a very abstract idea of "any kind of context of any type of elements"
trait Ctx[F[_]]
As another analogy consider the following
box of chocolate -> proper type -> case class Box(v: Chocolate)
box of _ -> type constructor of first order -> case class Box[A](v: A)
_ of _ -> type constructor of higher order -> trait Ctx[F[_]]
Now focus on _ of _. Here we have "something of something", which kind of seems like we have nothing. How can we do anything with this? This is where the idea of a type class comes into play. A type class can constrain a highly polymorphic shape such as F[_]
def apply[F[_]: Sync](...)
Here [F[_]: Sync] represents this constraint. It means that method apply accepts any type constructor of first kind for which there exists evidence that it satisfies the constraints of type class Sync. Note that type class Sync
trait Sync[F[_]]
is considered a higher order type constructor, whilst type parameter F[_] represents a first order type constructor. Similarly
F[_] : Sync : Concurrent
specifies that type constructor F must not only satisfy Sync constraints, but also constraints of Concurrent type class, and so on. These techniques are sometimes referred to as scary sounding
higher order type constructor polymorphism
and yet I am confident that most programmers have all the conceptual tools already present to understand it because
if you ever passed a function to a function, then you can work with concept of higher order
if you ever used a List, then you can work with concept of type constructors
if you ever wrote a method that uses the same implementation for both Integers and Doubles, then you can work with concept of polymorphism
Providing evidence that a type constructor satisfies constraints of a type class are given using Scala's implicit mechanisms. IMO Scala 3 has significantly simplified the concept so consider https://dotty.epfl.ch/docs/reference/contextual/type-classes.html
trait foo[F] {
def test: F
}
class ah extends foo[(Int,Int) => Int] {
def test = (i: Int,j: Int) => i+j
}
So the question is, why Scala known to be so smart about types cannot just deduce type (Int,Int) => Int from the type of test asking me instead to specify it? Or It is still possible? Or maybe this is backed by some thoughts that I don't have in mind.
Your question is basically "why does Scala have only local type inference" or equivalently "why does Scala not have non-local type inference". And the answer is: because the designers don't want to.
There are several reasons for this. One reason is that the most reasonable alternative to local type inference is global type inference, but that's impossible in Scala: Scala has separate compilation and modular type checking, so there simply is no point in time where the compiler has a global view of the entire program. In fact, Scala has dynamic code loading, which means that at compile time the entire code doesn't even need to exist yet! Global type inference is simply impossible in Scala. The best we could do is "whole-compilation-unit type inference", but that is undesirable, too: it would mean that whether or not you need type annotations depends on whether or not you compile your code in multiple units or just one.
Another important reason is that type annotations at module boundaries serve as a double-check, kind of like double-entry book keeping for types. Note that even in languages with global type inference like Haskell, it is strongly recommended to put type annotations on module boundaries and public interfaces.
A third reason is that global type inference can sometimes lead to confusing error messages, when instead of the type checker failing at a type annotation, the type inferencer happily chugs along inferring increasingly non-sensical types, until it finally gives up at a location far away from the actual error (which might just be a simple typo) with a type error that is only tangentially related to the original types at the error site. This can sometimes happen in Haskell, for example. The Scala designers value helpful error messages so much that they are willing to sacrifice language features unless they can figure out how to implement them with good error messages. (Note that even with Scala's very limited type inference, you can get such "helpful" messages as "expected Foo got Product with Serializable".)
However, what Scala's local type inference can do in this example, is to work in the other direction, and infer the parameter types of the anonymous function from the return type of test:
trait foo[F] {
def test: F
}
class ah extends foo[(Int, Int) ⇒ Int] {
def test = (i, j) ⇒ i + j
}
(new ah) test(2, 3) //=> 5
For you particular example, inferring type parameters based on inheritance can be ambiguous:
trait A
trait B extends A
trait Foo[T] {
val x: T
}
trait Bar extends Foo[?] {
val x: B
}
The type that could go in the ? could be either A or B. This is a general example of where Scala will not infer types that could be ambiguous.
Now, you are correct to observe that there is a similarity between
class Foo[T](x: T)
and
trait Foo[T] { x: T }
I have seen work by some into possibly generalizing the similarity (but I can't seem to find it right now). That could, in theory, allow type inference of type parameters based on a member. But I don't know if it will ever get to that point.
Sometimes one might want to declare x to be of the same type as y. With vals type inference handles this very well, but this does not work in some other areas, like with function types.
A solution which seems obvious to a programmer with some C++ experience would be a decltype. No such facility seems to be present in current Scala.
An answer to the linked questions tells:
because types are not first class citizens
I have to admit I do not understand this. I do not think types are a first class citizens in C++, but still it can have the decltype. I am not asking about anything like decltype for type parameters in generics or anything like that (I understand generics are not templates and the types are erased in them). Still, I think an operator which would allow me to use a type of an expression in a place where a type is expected - certainly the compiler must be able to evaluate an expression type, otherwise type inference for val definition would not be possible.
A decltype could be used like below - the code is not trying to do anything anything useful, just to illustrate the syntax and basic usage:
case class A(x:Int = 0)
val a = new A(10)
val b = new decltype(a)
def f(c:decltype(a)) : decltype(a.x+a.x)
Is absence of decltype a deliberate decision, or are there some specific reasons why Scala cannot have it? Is there perhaps some solution using compile time reflection which would allow this?
My first stab:
class Decl[T] { type Type = T }
object Decl { def apply[T](x: T) = new Decl[T] }
For example, if we have some variable x whose type we don't want to state explicitly:
val d = Decl(x)
type TypeOfX = d.Type
So I'm trying to work through Norvig & Russell's "Artificial Intelligence, A Modern Approach" as a way to learn Scala. I have a pretty good grasp on the language basics at this point, but I still find myself often "fighting" the type system.
Long story short, breadth-first and depth-first search algorithms are the same aside from the mechanics of pushing/popping to their underlying collection. Depth-first would prepend new possibilities and use a Stack, while Breadth-first would append and use a Queue.
To keep my algorithm the same, I created a typeclass called "GiveGrab" (I know, horrible name) with the intention of pimping ... err ... enriching collections with these "default" push (give) and pop-like (grab) operations.For example, grab would result in a call to .dequeue() for queues, and .pop() for stacks.
Here's (a somewhat abbreviated version of) the code:
object Example extends App {
trait GiveGrab[A, M[A]] {
def give(x: A*): M[A]
def grab(): A
}
implicit class GiveGrabQueue[T](q: Queue[T]) extends GiveGrab[T,Queue[T]] {
override def give(x: T*) = q ++= x
override def grab() = q.dequeue()
}
class TestClass[T, X <% GiveGrab[T, Queue[T]]](var storage: X) {}
val test = new TestClass[Int, Queue[Int]](new Queue[Int]())
}
When trying to compile this, I get the following errors:
Error:(18, 39) scala.collection.mutable.Queue[T] takes no type parameters, expected: one
class TestClass[T, X <% GiveGrab[T, Queue[T]]](var storage: X) {}
^
Error:(13, 67) scala.collection.mutable.Queue[T] takes no type parameters, expected: one
implicit class GiveGrabQueue[T](q: Queue[T]) extends GiveGrab[T,Queue[T]] {
^
That said, it took me a lot of trial and error to even get to this point. I'm not sure if my trait is really supposed to be typed
trait GiveGrab[A, M[A]]
or
trait GiveGrab[A, M[_]]
or
trait GiveGrab[A, M]
The error "takes no type parameters, expected: one" doesn't make a whole lot of sense to me at this point, and there's only a handful of other posts about that message (some related to dependent types, and some related to the Play framework).
Somewhat related: is there a good article for understanding Scala type signatures? I read through Programming in Scala 2nd Ed, but it didn't really touch on this sort of type gymnastics (either that, or I just missed it.)
Edit: Typos
What #PatrykĆwiek proposed is not a workaround but actually what you are meant to be doing: M[A] in trait GiveGrab defines a type function. Roughly speaking this means: M is a type where you can apply a single type parameter to yield a concrete type. That the parameter is called A is pure coincidence. The following means the same:
trait GiveGrab[A,M[MyRandomName]] { ... }
In the definition of give, you actually use this type function to create a type, when saying M[A]. Therefore, as #PatrykĆwiek said, you should write Queue instead of Queue[T]. While Queue is precisely one of these type functions, Queue[T] is a concrete type and therefore doesn't apply to the definition of M.
The error message you get says exactly that: In the place of M, you are supposed to put a type that takes a parameter (like Queue), but you have put one which takes none (Queue[T] in your case, another example would be String or Int).