I have the following case class.
case class CustomAttributeInfo[T,Y](
attribute:MyAttribute[_],
fieldName:String,
valueParser:T => Y){}
The case class takes three values.
The last argument is a function that will parse an input of any type and return the part of the input we wish to keep.
(Imagine, for just one example, I pass in a jsonstring, convert to json object, and extract an Int).
The companion object will supply a range of functions that we can pass to the case class. The one shown here, simply takes the input as a string and returns it as a string (the most simple possible example).
object CustomAttributeInfo {
val simpleString = (s:String) => s
}
I create the case class as follows:
CustomAttributeInfo(MyAttribute(var1, var2), name, CustomAttributeInfo.simpleString)
Later, I call the function 'valueParser'
customAttributeInfo.valueParser(k)
Compilation error
Error:(366, 69) type mismatch;
found : k.type (with underlying type String)
required: _$13
case Some(info) => Some((info.attribute, info.valueParser(k)))
I am not a generics expert (obviously). I have done some reading, but I have not seen a discussion about a case like this. Any advice and explanation would be most welcome
You haven't provide enough information to answer your question.
The following code compiles.
If you still have compile error provide MCVE.
case class MyAttribute[_](var1: Any, var2: Any)
case class CustomAttributeInfo[T,Y](attribute:MyAttribute[_], fieldName:String, valueParser:T => Y) {}
object CustomAttributeInfo {
val simpleString = (s:String) => s
}
val var1: Any = ???
val var2: Any = ???
val name: String = ???
val customAttributeInfo = CustomAttributeInfo(MyAttribute(var1, var2), name, CustomAttributeInfo.simpleString)
val k = "abc"
customAttributeInfo.valueParser(k)
#Dmytro was right that a simple example compiled. In my actual codebase code, however, we needed to be specific about the type.
This worked:
object CustomAttributeInfo {
type T = Any
val simpleString = (s:T) => s.toString
}
Related
I'm fairly new to Scala in general, and Scala 3 in particular, and I'm trying to write some code that deals with transparently encoding + decoding values before they are passed to another library.
Basically, I need to map a set of types like Ints to a counterpart in the underlying library. The code I've written is too verbose to replicate here in full, but here's a minimal example demonstrating the kind of thing, using a higher-kinded Encoder type that encapsulates encoding values into types which depend on the values' original types:
trait Encoder[T] {
type U
def encode(v: T): U
}
object Encoder {
given Encoder[Int] with {
override type U = String
override def encode(v: Int): String = v.toString
}
}
case class Value[T : Encoder](v: T) {
val encoder: Encoder[T] = summon[Encoder[T]]
}
I also need to be able to write functions that deal with specific types of Value and which have 'concrete' return types. Like this:
def doStuff(v1: Value[Int]): String = {
v1.encoder.encode(v1.v)
}
However, even though in this case v1.codec.encode does indeed return a String, I get an error:
-- [E007] Type Mismatch Error: -------------------------------------------------
2 | v1.encoder.encode(v1.v)
| ^^^^^^^^^^^^^^^^^^^^^^^
| Found: v1.encoder.U
| Required: String
What can I do differently to solve this error? Really appreciate any pointers to help a newbie out š
Answering the question in the comments
Is there any sensible way I tell the compiler that Iām only interested in Values with Encoders that encode to String?
You can force Value to remember its encoder's result type with an extra type argument.
case class Value[T, R](val v: T)(
using val encoder: Encoder[T],
val eqv: encoder.U =:= R,
)
The encoder is the same as your encoder, just moved to the using list so we can use it in implicit resolution.
eqv is a proof that R (our type parameter) is equivalent to the encoder's U type.
Then doStuff can take a Value[Int, String]
def doStuff(v1: Value[Int, String]): String = {
v1.eqv(v1.encoder.encode(v1.v))
}
Let's be clear about what's happening here. v1.encoder.encode(v1.v) returns an encoder.U. Scala isn't smart enough to know what that is. However, we also have a proof that encoder.U is equal to String, and that proof can be used to convert an encoder.U to a String. And that's exactly what =:=.apply does.
We have to do this back in the case class because you've already lost the type information by the time we hit doStuff. Only the case class (which instantiates the implicit encoder) knows what the result type is, so we need to expose it there.
If you have other places in your codebase where you don't care about the result type, you can fill in a type parameter R for it, or use a wildcard Value[Int, ?].
I would also suggest giving Match Types a try if we are only talking about Scala 3 here.
import scala.util.Try
type Encoder[T] = T match
case Int => String
case String => Either[Throwable, Int]
case class Value[T](v: T):
def encode: Encoder[T] = v match
case u: Int => u.toString
case u: String => Try(u.toInt).toEither
object Main extends App:
val (v1, v2) = (Value(1), Value(2))
def doStuff(v: Value[Int]): String =
v.encode
println(doStuff(v1) + doStuff(v2)) //12
println(Value(v1.encode).encode) //Right(1)
On a recent worksheet I was presented with the question asking what would be the output of the following code:
class A { def m(x:Double) = x+x }
class B[Any] extends A{ def m(x: Any) = print(x) }
class C[Any] { def m (x:Double) = x+x; def m (x: Any) = print(x) }
val obj1 = new B[Int]; val obj2 = new C[Any]
obj1.m(1); obj1.m(2.3); obj2.m(4); obj2.m(5.6)
I'm quite confused as to what having a concrete type in the square brackets after the class name would mean (i.e. class B[Any]). Is the later expression val obj1 = new B[Int] valid because Int <: Any, Int being a subclass of Any?
When later running the code snippet, the result given was simply "1" being printed. This was not what I had expected the call to obj.m(2.3) to resolve at def m(x: any), where it seems in actuality the compiler went up to A and called the m in class A.
The later expressions, obj2.m(4) and obj2.m(5.6) seems to make sense as both 4 and 5.6 would land in the function with def m(x: Double), thus not print anything out.
In what order exactly does the compiler traverse to find what to call? I'd be very grateful if someone could clear up my confusions with how polymorphism is handled here by Scala, thank you very much :)
When you do class B[Any], you define a class with a type parameter called Any. Don't confuse the type parameter name with the actual class Any. You are just shadowing its name.
You could just as fine do this:
class B[Int]
val obj = new B[String]
You may see why it is bad practice to name type parameters after actual types. Usually, people use single letter names for their type parameters, like this:
class B[T] // I just changed the name of the type parameter from "Int" to "T".
val obj = new B[String]
I'm new to scala.
What does following syntax mean?
case class User(val id: Long)(val username: String)
I've read about currying in scala, but explain please how it related to construction above (if related).
Thanks.
Just like partially-applied functions, a constructor (which is a function from its arguments to the constructed type) can be partially applied, in this case:
scala> case class User(val id: Long)(val username: String)
defined class User
scala> val userBuilder = User(123L) _
userBuilder: String => User = <function1>
Note the type of the resulting userBuilder - it's a function from String (the remaining parameter) to User
Now, like partially applied functions, you can apply this result to a String and get a User instance:
scala> val user = userBuilder("a")
user: User = User(123)
scala> user.username
res1: String = a
When is this useful?
When you want to construct many instances with common values for a subset of the arguments, e.g.:
case class Person(lastName: String)(val firstName: String)
class Family(lastName: String, firstNames: List[String]) {
def getMembers: List[Person] = {
val creator = Person(lastName) _ // will be reused for each first name!
firstNames.map(creator)
}
}
When you want to use one argument as the default value of another one:
case class Measure(min: Int)(val max: Int = min*2)
Measure(5)() // max = 10
Measure(5)(12) // default overridden, max = 12
When you want to use implicit arguments, which must reside in a separate, last argument list of the function, as described int the Scala Language Specification (Chapter 7.2):
A method or constructor can have only one implicit
parameter list, and it must be the last parameter list given.
It allows you to construct the object in steps.
val user = User(123L) _ // user now has only the ID
// later on
val completeUser = user("moreo") // user now also has the username
This is generally useful when you want to have your object follow an interface, but need to pass additional parameters, so you first initialise your object with those parameters and then later you get a function that can follow the interface.
I'm trying to implement something like clever parameters converter function with Scala.
Basically in my program I need to read parameters from a properties file, so obviously they are all strings and I would like then to convert each parameter in a specific type that I pass as parameter.
This is the implementation that I start coding:
def getParam[T](key : String , value : String, paramClass : T): Any = {
value match {
paramClass match {
case i if i == Int => value.trim.toInt
case b if b == Boolean => value.trim.toBoolean
case _ => value.trim
}
}
/* Exception handling is missing at the moment */
}
Usage:
val convertedInt = getParam("some.int.property.key", "10", Int)
val convertedBoolean = getParam("some.boolean.property.key", "true", Boolean)
val plainString = getParam("some.string.property.key", "value",String)
Points to note:
For my program now I need just 3 main type of type: String ,Int and Boolean,
if is possible I would like to extends to more object type
This is not clever, cause I need to explicit the matching against every possibile type to convert, I would like an more reflectional like approach
This code doesn't work, it give me compile error: "object java.lang.String is not a value" when I try to convert( actually no conversion happen because property values came as String).
Can anyone help me? I'm quite newbie in Scala and maybe I missing something
The Scala approach for a problem that you are trying to solve is context bounds. Given a type T you can require an object like ParamMeta[T], which will do all conversions for you. So you can rewrite your code to something like this:
trait ParamMeta[T] {
def apply(v: String): T
}
def getParam[T](key: String, value: String)(implicit meta: ParamMeta[T]): T =
meta(value.trim)
implicit case object IntMeta extends ParamMeta[Int] {
def apply(v: String): Int = v.toInt
}
// and so on
getParam[Int](/* ... */, "127") // = 127
There is even no need to throw exceptions! If you supply an unsupported type as getParam type argument, code will even not compile. You can rewrite signature of getParam using a syntax sugar for context bounds, T: Bound, which will require implicit value Bound[T], and you will need to use implicitly[Bound[T]] to access that values (because there will be no parameter name for it).
Also this code does not use reflection at all, because compiler searches for an implicit value ParamMeta[Int], founds it in object IntMeta and rewrites function call like getParam[Int](..., "127")(IntMeta), so it will get all required values at compile time.
If you feel that writing those case objects is too boilerplate, and you are sure that you will not need another method in these objects in future (for example, to convert T back to String), you can simplify declarations like this:
case class ParamMeta[T](f: String => T) {
def apply(s: String): T = f(s)
}
implicit val stringMeta = ParamMeta(identity)
implicit val intMeta = ParamMeta(_.toInt)
To avoid importing them every time you use getParam you can declare these implicits in a companion object of ParamMeta trait/case class, and Scala will pick them automatically.
As for original match approach, you can pass a implicit ClassTag[T] to your function, so you will be able to match classes. You do not need to create any values for ClassTag, as the compiler will pass it automatically. Here is a simple example how to do class matching:
import scala.reflect.ClassTag
import scala.reflect._
def test[T: ClassTag] = classTag[T].runtimeClass match {
case x if x == classOf[Int] => "I'm an int!"
case x if x == classOf[String] => "I'm a string!"
}
println(test[Int])
println(test[String])
However, this approach is less flexible than ParamMeta one, and ParamMeta should be preferred.
Hello there Stack Overflow,
I hope you'll help me with my very first question here :)
So I'm having a problem with Scala type inference. Here is the code:
object Problem {
def ok(fn: (String) => Unit) = fn("")
// type mismatch; found: java.lang.String("") required: String
def fail[String](fn: (String) => Unit) = fn("")
}
What kind of String does Scala expect here?
Note that this is a minimal example to explain my problem. The original issue appeared when I tried to implement a more complex interface (Play's Iteratee, to be precise), so, no, leaving out the [String] is not an option. (If anyone thinks that the actual code would help, I'll provide it.)
I tried def fail[java.lang.String] ... but then it says expected ], found ..
I did read Scala String vs java.lang.String - type inference which gives a good explanation on java.lang.String vs. scala.Predef.String, but I still could not come up with a solution for my specific problem.
Any ideas?
EDIT: So here is the original attempt how I tried to implement http://www.playframework.org/documentation/api/2.0/scala/play/api/libs/iteratee/Iteratee.html only that I wrote String instead of T. (With T it compiles, and it makes sense!) My fail; obviously I was a bit overwhelmed by all the type parameters:
val stream = WS.url("url").get({ headers =>
(new Iteratee[Array[Byte], String] {
def fold[T](done: (String, Input[Array[Byte]]) => Promise[T],
cont: (Input[Array[Byte]] => Iteratee[Array[Byte], String]) => Promise[T],
error: (String, Input[Array[Byte]]) => Promise[T]): Promise[T] =
{
done("something", Input.Empty)
}
})
})
Regards,
Hendrik
When you write:
def fail[String](fn: (String) => Unit) = fn("")
The type parameter between square brackets String is just an arbitrary name that, in your case, will hide scala or java string. It is fully equivalent to:
def fail[T](fn: (T) => Unit) = fn("")
If you want to constrain the type to string, you just have to write:
def fail(fn: (String) => Unit) = fn("")
And it will work for scala and java strings (since they are the same).
This problem has nothing to do with Java vs Scala strings.
In the line def fail[String](fn: (String) => Unit) = fn("") you're definining a completely new type parameter and naming it String. This shadows the general definition.
A type paramter is needed if you intend to abstract over the type. You are not doing this in the fail example, and this should be dropped.
If you are overriding something that uses a type parameter, then you should specify this at the class level:
class A extends B[String]
Looking at the code for Iteratee, I'll guess you're trying to implement fold with its various done, cont and error functions. Fold only has one type paramter, which is used in the return type, so I'm not sure where this can come from. The input type parameters are specified on the class, so if you extend Iteratee[String, Unit] these should be provided.