How is the following chisel statement decoded? - scala

I am learning SonicBoom. The following sentence should be based on the decoding table to assign the corresponding decoding signal.
class CtrlSigs extends Bundle
{
val legal = Bool()
val fp_val = Bool()
val fp_single = Bool()
val uopc = UInt(UOPC_SZ.W)
val iq_type = UInt(IQT_SZ.W)
val fu_code = UInt(FUC_SZ.W)
val dst_type = UInt(2.W)
val rs1_type = UInt(2.W)
val rs2_type = UInt(2.W)
val frs3_en = Bool()
val imm_sel = UInt(IS_X.getWidth.W)
val uses_ldq = Bool()
val uses_stq = Bool()
val is_amo = Bool()
val is_fence = Bool()
val is_fencei = Bool()
val mem_cmd = UInt(freechips.rocketchip.rocket.M_SZ.W)
val wakeup_delay = UInt(2.W)
val bypassable = Bool()
val is_br = Bool()
val is_sys_pc2epc = Bool()
val inst_unique = Bool()
val flush_on_commit = Bool()
val csr_cmd = UInt(freechips.rocketchip.rocket.CSR.SZ.W)
val rocc = Bool()
def decode(inst: UInt, table: Iterable[(BitPat, List[BitPat])]) = {
val decoder = freechips.rocketchip.rocket.DecodeLogic(inst, XDecode.decode_default, table)
val sigs =
Seq(legal, fp_val, fp_single, uopc, iq_type, fu_code, dst_type, rs1_type,
rs2_type, frs3_en, imm_sel, uses_ldq, uses_stq, is_amo,
is_fence, is_fencei, mem_cmd, wakeup_delay, bypassable,
is_br, is_sys_pc2epc, inst_unique, flush_on_commit, csr_cmd)
sigs zip decoder map {case(s,d) => s := d}
rocc := false.B
this
}
}
But I want to know how this step is achieved. sigs zip decoder map {case(s,d) => s := d}What does this code mean?
Zip and map seem to be functions within Vec. I tried to check its official manual.
https://www.chisel-lang.org/api/latest/chisel3/index.html?search=zip This manual is the most difficult manual I have ever seen.
He only gave the following explanation:
defzip[A1 >: T, B, That](that: GenIterable[B])(implicit bf: CanBuildFrom[IndexedSeq[T], (A1, B), That]): That
Definition Classes
IterableLike → GenIterableLike
defmap[B, That](f: (T) ⇒ B)(implicit bf: CanBuildFrom[IndexedSeq[T], B, That]): That
Definition Classes
TraversableLike → GenTraversableLike → FilterMonadic
I can't understand what the manual is saying at all? I checked Chisel's books again. There is still no introduction to these two functions. Can someone please introduce? Many thanks!

zip is a standard Scala method. It join two sequences together in a sequence of 2-Tuples (Couple of values). Think about jacket zipper.
map is also a standard scala method. It apply a function to each membre of the sequence.
The function given to map here is case(s,d) => s := d this function use pattern matching to get the two tuple members s and d.
The value returned by this function is an assignation (connection) of d to s: s := d.
For better understanding of Chisel, I think that we must know a bit of Scala before.

I have not attempted to compile it, and to be honest, I have only a vary vague understanding of what Chisel does at all, so this is just an educated guess:
sigs is the sequence Seq(legal, fp_val, ..., csr_cmd) defined in the line above
decoder is also a sequence of some entities that can appear on the right hand side of :=.
zip pairs sigs and decoder-sequences elementwise, i.e. [s1, s2, s3, ..., sN] and [d1, d2, d3, ..., dN] are zipped into a sequence of pairs [(s1, d1), (s2, d2), ..., (sN, dN)].
map goes through the sequence, and does something to each pair. Since the result is unused, I assume that it actually should have been a foreach.
case deconstructs the pairs into first and second components s and d
s := d somehow wires together s and d (if you are working with Chisel, you probably know better than me what the := means)
It could have been rewritten with a for-yield:
for ((s, d) <- sigs.zip(decoder))
yield s := d
but since the result is unused, it probably could have been written as
sigs.zip(decoder).foreach { case (s, d) => s := d }
or with the corresponding for-syntax:
for ((s, d) <- sigs.zip(decoder)) {
s := d
}
Regarding the manual entry: that's a signature which is quite typical for Scala collection libraries. The pile of generic parameters is a type-level specification that ensures that the type of the returned collection is "as precise as possible". This has the advantage that one can write generic methods for a whole bunch of collections, without having to return one-size-fits-all-Lists everywhere. Unfortunately, this also makes the signatures undecipherable to the novices.
This is the signature:
def zip[A1 >: T, B, That](that: GenIterable[B])(implicit bf: CanBuildFrom[IndexedSeq[T], (A1, B), That]): That
Here is how you read those signatures:
Identify all the type variables that occur only in the implicit parameters. In this case, those would be A1 and That.
Identify all the type variables that occur in the return type. In this case, it's the That.
Now:
If a type variable occurs only in the type of the implicit parameters, but not in the return type, then simply ignore them: this is just a way to specify the search space for the most appropriate implicit argument. You can ignore them, unless you are a library author who is attempting to plug-in your collections into the standard collection library. Consider it to be irrelevant library-author-to-library-author chatter.
If some type variable X occurs both in the type of the implicit parameters and in the return type, then you can assume that it's "the best possible X you can think of".
Applied to this signature:
T is the element type of your original vector
B is the element type of the first argument (the collection you are zipping with)
A1 occurs only in the type of the implicit parameter: ignore it.
That occurs in implicit parameter and in return type: assume it's "the best possible collection type".
All together, it gives you:
You are starting with a vector with elements of type T
You are zipping it with a sequence of elements of type B
You can assume that That will be a collection of the most precise type, something like Vec[(T, B)] or at least IndexedSeq[(T, B)]. What it is, doesn't really matter, because you are invoking foreach on it anyway.
More generally, the idea would be that you can simply rely on the type inference, and assume that it will figure out the most appropriate result types everywhere.

Related

Why does Scala sometimes ignore types that are clearly defined?

so here's the problem I keep running into various situations with Scala - it seemingly ignores the implied type, even when the situation is clear. Granted this could be my understanding I admit, but when it comes to underscore placeholders I keep running into trouble. For example below (this is fictional just to prove the point).The 2nd position of trait X has to be <:X[,] of some kind. There's no ambiguity here - so anywhere that scala sees this position, regardless of how weak it's coded - the contact is X and I should have access to functions like "doesX". Isn't that indisputable? No matter how poorly I deal with that position in the code, I must at least get X. Why does Scala constantly ignore this fact when you get deep into the type system? Any pointers would be appreciated, thank you!
object TestRun extends App {
trait X[T, Y<:X[_,_]] {
def doesX:Unit
def providesY:Y
}
class Test extends X[Int,Test]{
override def doesX: Unit = println("etc..")
def providesY:Test = new Test
}
val a:X[_,_] = new Test //yes I know I could define a better here, its just to demo. I shouldn't have to explicitly relabel the 2nd _ as _<:X[_,<:X[ etc..
val b = a.providesY //clearly this has to be at least a (something) WITH X, but scala claims this as "Any"
b.doesX //error won't compile!!
//trait
}
When you write:
val a: X[_, _] = new Test
^
// This is treated as if the type parameter is Any, for the most part
You are telling the compiler that a is an X, where you don't care what its type parameters are. That is, the unbounded wildcard _ is assumed to have an upper-bound of Any, and that's it.
providesY uses the second type parameter of X to determine its return type, but for a the compiler was told that to discard it. So b is just an Any. This is easier to see using the REPL:
scala> val a: X[_, _] = new Test
a: X[_, _] = Test#27abe2cd
scala> val b = a.providesY
b: Any = Test#f5f2bb7
Therefore, b.doesX fails to compile because the compiler now thinks it is Any. The simple solution is not to use wild cards for types (or any existential types in general, most of the time you do not want this).
scala> val a: X[Int, Test] = new Test
a: X[Int,Test] = Test#1134affc
scala> val b = a.providesY
b: Test = Test#6fc6f14e
scala> b.doesX
etc..
Or you could simply leave off the type annotation, and let the compiler infer the correct type.

HList/KList from class values

I want to be able to create a class/trait that behaves somewhat like an enumeration (HEnum in the first snippet below). I can't use a plain enumeration because each enum value could have a different type (though the container class will be the same): Key[A]. I'd like to be able to construct the enum roughly like this:
class Key[A](val name: String)
object A extends HEnum {
val a = new Key[String]("a")
val b = new Key[Int]("b")
val c = new Key[Float]("c")
}
And then I'd like to be able to perform more or less basic HList operations like:
A.find[String] // returns the first element with type Key[String]
A.find("b") // returns the first element with name "b", type should (hopefully) be Key[Int]
So far I've been playing with an HList as the underlying data structure, but constructing one with the proper type has proven difficult. My most successful attempt looks like this:
class Key[A](val name: String)
object Key {
def apply[A, L <: HList](name: String, l: L): (Key[A], Key[A] :: L) = {
val key = new Key[A](name)
(key, key :: l)
}
}
object A {
val (a, akeys) = Key[String, HNil]("a", HNil)
val (b, bkeys) = Key[Int, Key[String] :: HList]("b", akeys)
val (c, ckeys) = Key[Float, Key[Int] :: HList]("c", bkeys)
val values = ckeys // use this for lookups, etc
def find[A]: Key[A] = values.select[A]
def find[A](name: String): Key[A] = ...
}
The problem here is that the interface is clunky. Adding a new value anywhere besides the end of the list of values is error prone and no matter what, you have to manually update values any time a new value is introduced. My solution without HList involved a List[Key[_]] and error prone/unsafe casting to the proper type when needed.
EDIT
I should also mention that the enum example found here is not particularly helpful to me (although, if that can be adapted, then great). The added compiler checks for exhaustive pattern matches are nice (and I would ultimately want that) but this enum still only allows a homogeneous collection of enum values.

Scala - Make signature of function parameter f of higher order function g dependent on varars of g

I am trying to define a higher order function f which accepts a variable number of parameters args of type Wrapper[T]* and a function parameter g in Scala.
The function f should decapsulate each object passed in args and then call g with the decapsulated parameters. Therefore, g has to accept exactly the same number of parameters of type T as args contains.
The closest thing I could achieve was to pass a Seq[T] to g and to use pattern matching inside of g. Like the following:
f("This", "Is", "An", "Example")(x => x match {
case Seq(a:String, b:String, c:String): //Do something.
})
With f defined like:
def f[V](args: Wrapper[T]*)
(g: (Seq[T]) => (V)) : V = {
val params = args.map(x => x.unwrap())
g(params)
}
How is it possible to accomplish a thing like this without pattern
matching?
It is possible to omit the types in the signature of g
by using type inference, but only if the number of parameters is
fixed. How could this be done in this case?
It is possible to pass
different types of parameters into varargs, if a type wildcard is
used args: Wrapper[_]*. Additionally, casting the result of
x.unwrap to AnyRef and using pattern matching in g is
necessary. This, however, completely breaks type inference and type
safety. Is there a better way to make mixing types in the varargs
possible in this special case?
I am also considering the use of scala makros to accomplish these tasks.
Did I get you right? I replaced your Wrapper with some known type, but that doesn't seem to be essential.
def f[T, V](args: T*)(g: PartialFunction[Seq[T], V]): V = g(args)
So later you can do this:
f(1,2,3) { case Seq(a,b,c) => c } // Int = 3
Okay, I've made my own Wrapper to be totally clear:
case class Wrapper[T](val x:T) {
def unwrap = x
}
def f[V](args: Wrapper[_]*)(g: PartialFunction[Seq[_], V]): V =
g(args.map(_.unwrap))
f(Wrapper("1"), Wrapper(1), Wrapper(BigInt(1))) {
case Seq(s: String, i: Int, b: BigInt) => (s, i, b)
} // res3: (String, Int, BigInt) = (1,1,1)
Regarding your concerns about type safety and conversions: as you can see, there aren't any explicit conversions in the code above, and since you are going to pattern-match with explicitly defined types, you may not to worry about these things - if some items of an undefined origin are going to show in your input, scala.MatchError will be thrown.

Impredicative types vs. plain old subtyping

A friend of mine posed a seemingly innocuous Scala language question last week that I didn't have a good answer to: whether there's an easy way to declare a collection of things belonging to some common typeclass. Of course there's no first-class notion of "typeclass" in Scala, so we have to think of this in terms of traits and context bounds (i.e. implicits).
Concretely, given some trait T[_] representing a typeclass, and types A, B and C, with corresponding implicits in scope T[A], T[B] and T[C], we want to declare something like a List[T[a] forAll { type a }], into which we can throw instances of A, B and C with impunity. This of course doesn't exist in Scala; a question last year discusses this in more depth.
The natural follow-up question is "how does Haskell do it?" Well, GHC in particular has a type system extension called impredicative polymorphism, described in the "Boxy Types" paper. In brief, given a typeclass T one can legally construct a list [forall a. T a => a]. Given a declaration of this form, the compiler does some dictionary-passing magic that lets us retain the typeclass instances corresponding to the types of each value in the list at runtime.
Thing is, "dictionary-passing magic" sounds a lot like "vtables." In an object-oriented language like Scala, subtyping is a much more simple, natural mechanism than the "Boxy Types" approach. If our A, B and C all extend trait T, then we can simply declare List[T] and be happy. Likewise, as Miles notes in a comment below, if they all extend traits T1, T2 and T3 then I can use List[T1 with T2 with T3] as an equivalent to the impredicative Haskell [forall a. (T1 a, T2 a, T3 a) => a].
However, the main, well-known disadvantage with subtyping compared to typeclasses is tight coupling: my A, B and C types have to have their T behavior baked in. Let's assume this is a major dealbreaker, and I can't use subtyping. So the middle ground in Scala is pimps^H^H^H^H^Himplicit conversions: given some A => T, B => T and C => T in implicit scope, I can again quite happily populate a List[T] with my A, B and C values...
... Until we want List[T1 with T2 with T3]. At that point, even if we have implicit conversions A => T1, A => T2 and A => T3, we can't put an A into the list. We could restructure our implicit conversions to literally provide A => T1 with T2 with T3, but I've never seen anybody do that before, and it seems like yet another form of tight coupling.
Okay, so my question finally is, I suppose, a combination of a couple questions that were previously asked here: "why avoid subtyping?" and "advantages of subtyping over typeclasses" ... is there some unifying theory that says impredicative polymorphism and subtype polymorphism are one and the same? Are implicit conversions somehow the secret love-child of the two? And can somebody articulate a good, clean pattern for expressing multiple bounds (as in the last example above) in Scala?
You're confusing impredicative types with existential types. Impredicative types allow you to put polymorphic values in a data structure, not arbitrary concrete ones. In other words [forall a. Num a => a] means that you have a list where each element works as any numeric type, so you can't put e.g. Int and Double in a list of type [forall a. Num a => a], but you can put something like 0 :: Num a => a in it. Impredicative types is not what you want here.
What you want is existential types, i.e. [exists a. Num a => a] (not real Haskell syntax), which says that each element is some unknown numeric type. To write this in Haskell, however, we need to introduce a wrapper data type:
data SomeNumber = forall a. Num a => SomeNumber a
Note the change from exists to forall. That's because we're describing the constructor. We can put any numeric type in, but then the type system "forgets" which type it was. Once we take it back out (by pattern matching), all we know is that it's some numeric type. What's happening under the hood, is that the SomeNumber type contains a hidden field which stores the type class dictionary (aka. vtable/implicit), which is why we need the wrapper type.
Now we can use the type [SomeNumber] for a list of arbitrary numbers, but we need to wrap each number on the way in, e.g. [SomeNumber (3.14 :: Double), SomeNumber (42 :: Int)]. The correct dictionary for each type is looked up and stored in the hidden field automatically at the point where we wrap each number.
The combination of existential types and type classes is in some ways similar to subtyping, since the main difference between type classes and interfaces is that with type classes the vtable travels separately from the objects, and existential types packages objects and vtables back together again.
However, unlike with traditional subtyping, you're not forced to pair them one to one, so we can write things like this which packages one vtable with two values of the same type.
data TwoNumbers = forall a. Num a => TwoNumbers a a
f :: TwoNumbers -> TwoNumbers
f (TwoNumbers x y) = TwoNumbers (x+y) (x*y)
list1 = map f [TwoNumbers (42 :: Int) 7, TwoNumbers (3.14 :: Double) 9]
-- ==> [TwoNumbers (49 :: Int) 294, TwoNumbers (12.14 :: Double) 28.26]
or even fancier things. Once we pattern match on the wrapper, we're back in the land of type classes. Although we don't know which type x and y are, we know that they're the same, and we have the correct dictionary available to perform numeric operations on them.
Everything above works similarly with multiple type classes. The compiler will simply generate hidden fields in the wrapper type for each vtable and bring them all into scope when we pattern match.
data SomeBoundedNumber = forall a. (Bounded a, Num a) => SBN a
g :: SomeBoundedNumber -> SomeBoundedNumber
g (SBN n) = SBN (maxBound - n)
list2 = map g [SBN (42 :: Int32), SBN (42 :: Int64)]
-- ==> [SBN (2147483605 :: Int32), SBN (9223372036854775765 :: Int64)]
As I'm very much a beginner when it comes to Scala, I'm not sure I can help with the final part of your question, but I hope this has at least cleared up some of the confusion and given you some ideas on how to proceed.
#hammar's answer is perfectly right. Here is the scala way of doint it. For the example i'll take Show as the type class and the values i and d to pack in a list :
// The type class
trait Show[A] {
def show(a : A) : String
}
// Syntactic sugar for Show
implicit final class ShowOps[A](val self : A)(implicit A : Show[A]) {
def show = A.show(self)
}
implicit val intShow = new Show[Int] {
def show(i : Int) = "Show of int " + i.toString
}
implicit val stringShow = new Show[String] {
def show(s : String) = "Show of String " + s
}
val i : Int = 5
val s : String = "abc"
What we want is to be able run the following code
val list = List(i, s)
for (e <- list) yield e.show
Building the list is easy but the list won't "remember" the exact type of each of its elements. Instead it will upcast each element to a common super type T. The more precise super super type between String and Int being Any, the type of the list is List[Any].
The problem is: what to forget and what to remember? We want to forget the exact type of the elements BUT we want to remember that they are all instances of Show. The following class does exactly that
abstract class Ex[TC[_]] {
type t
val value : t
implicit val instance : TC[t]
}
implicit def ex[TC[_], A](a : A)(implicit A : TC[A]) = new Ex[TC] {
type t = A
val value = a
val instance = A
}
This is an encoding of the existential :
val ex_i : Ex[Show] = ex[Show, Int](i)
val ex_s : Ex[Show] = ex[Show, String](s)
It pack a value with the corresponding type class instance.
Finally we can add an instance for Ex[Show]
implicit val exShow = new Show[Ex[Show]] {
def show(e : Ex[Show]) : String = {
import e._
e.value.show
}
}
The import e._ is required to bring the instance into scope. Thanks to the magic of implicits:
val list = List[Ex[Show]](i , s)
for (e <- list) yield e.show
which is very close to the expected code.

Scala compiler not recognizing a view bound

I've tried this line of code
def **[A <% Numeric[A]](l:List[A],m:List[A])=l.zip(m).map({t=>t._1*t._2})
However on compilation, I get this error
error: value * is not a member of type parameter A
def **[A <% Numeric[A]](l:List[A],m:List[A])=l.zip(m).map({t=>t._1*t._2})
When I look at the source for the Numeric trait, I see a * op defined.
What am I doing wrong?
The instance of Numeric is not a number itself, but it is an object that offers operations to do the arithmetic. For example, an object num of type Numeric[Int] can add two integers like this: num.plus(3, 5) The result of this operation is the integer 7.
For integers, this is very trivial. However, for all basic numerical types, there is one implicit instance of Numeric available. And if you define your own numeric types, you can provide one.
Therefore, you should leave the bounds for A open and add an implicit parameter of type Numeric[A], with which you do the calculations. Like this:
def **[A](l:List[A],m:List[A])(implicit num:Numeric[A])=l.zip(m).map({t=>num.times(t._1, t._2)})
Of course, num.times(a,b) looks less elegant than a*b. In most of the cases, one can live with that. However, you can wrap the value a in an object of type Ops that supports operators, like this:
// given are: num:Numeric[A], a:A and b:A
val a_ops = num.mkNumericOps(a)
val product = a_ops * b
Since the method mkNumericOps is declared implicit, you can also import it and use it implicitly:
// given are: num:Numeric[A], a:A and b:A
import num._
val product = a * b
You can also solve this with a context bound. Using the context method from this answer, you can write:
def **[A : Numeric](l:List[A],m:List[A]) =
l zip m map { t => context[A]().times(t._1, t._2) }
or
def **[A : Numeric](l:List[A],m:List[A]) = {
val num = context[A]()
import num._
l zip m map { t => t._1 * t._2 }
}