How to Map with a case class in Scala - scala

I am trying to convert, a Map with the case class I defined using the Apply.
case class ETLConfig (srcTableName:Option[String] = None,
idCol:Option[String] = None ,
orderingCol:Option[String] = None)
object ETLConfig {
def apply(map: Map[String, String]): ETLConfig =
ETLConfig(map.get("srcTableName"),map.get("idCol"),map.get("orderCol"))
}
Error:
command-512604125416347:2: error: too many arguments for method apply: (map: Map[String,String])ETLConfig in object ETLConfig
def apply(map: Map[String, String]):ETLConfig = ETLConfig(map.get("srcTableName"),map.get("idCol"),map.get("orderCol"))
Can someone please point me the whats wrong I am doing.
Also I tried the below and is working.
val empMap:Map[String, String] = Map("srcTableName" -> "Employee","idCol" -> "EmpId", "orderCol" -> "CreatedOn" )
val empConfig = ETLConfig(dtMap.get("srcTableName"),dtMap.get("idCol"), dtMap.get("orderCol"))
val srcTable = dtConfig.srcTableName.orNull

EDIT: Since you're using Databricks, you should define your classes and objects in package cells.
Original Answer:
I assume you're doing this in a REPL. A case class and its companion object need to be in the same file. In the usual Scala REPL, each command counts as a new "file." This means that your ETLConfig object is not a companion to your case class, with the result that it shadows your apply method instead of overloading it (and you can't call the original one as a result).
In the official Scala REPL, you can solve this by using the :paste command:
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class ETLConfig (srcTableName:Option[String] = None,
idCol:Option[String] = None ,
orderingCol:Option[String] = None)
object ETLConfig {
def apply(map: Map[String, String]): ETLConfig =
ETLConfig(map.get("srcTableName"),map.get("idCol"),map.get("orderCol"))
}
// Exiting paste mode, now interpreting.
defined class ETLConfig
defined object ETLConfig
From your output, it looks like you're using a different REPL, so I don't know if this will work.

Related

Pimp my library on Joda LocalDate does not work

Here I am adding a new method to to LocalDate:
object EnhancedDate {
implicit class EnhancedDate(start: org.joda.time.LocalDate) {
/** Generates dates between start and end - inclusive */
def to(end: org.joda.time.LocalDate): IndexedSeq[org.joda.time.LocalDate] = {
val numberOfDays = Days.daysBetween(start, end.plusDays(1)).getDays()
for (f <- 0 to numberOfDays) yield start.plusDays(f)
}
}
}
and now using it:
import cats.effect.IO
import EnhancedDate.EnhancedDate
def loadCadUsdRates: IO[Map[String, CadInUsd]] = IO {
val start = new org.joda.time.LocalDate(startYear,1,1)
val end = new org.joda.time.LocalDate(endYear+1,1,1).minusDays(1)
start to end map(f) toMap
}
Function f doesn't really matter; you can use identity for all it's worth bc the results are the same.
I use Idea and everything is fine in the IDE (no red underlyings) but when I run it I get this error:
Error:(104, 9) value to is not a member of org.joda.time.LocalDate
start to end map(f) toMap
Now I've used pimp my library before and I'm pretty sure this is the right way to do it, but there must be something about joda date that is incompatible with it... Have you tried pimping Joda LocalDate.. I do not know why this is not working
I'm not exactly sure what is going on here but it's something to do with the declaration being a module with the same name as the implicit class within it. This means that you have two identifiers in scope, both called EnhancedDate and I assume that this must cause resolution ambiguity. It's certainly nothing to do with Joda. One definite workaround is to place your implicit class inside the nearest package object.
package object myPackage {
implicit class EnhancedDate(start: org.joda.time.LocalDate) { ... }
}
Here's a demonstration of it not working:
scala> :paste
// Entering paste mode (ctrl-D to finish)
object Foo {
implicit class Foo(s: String) { def foo = println(s"Foo: $s") }
}
// Exiting paste mode, now interpreting.
defined object Foo
scala> import Foo._
import Foo._
scala> "hey".foo
<console>:14: error: value foo is not a member of String
"hey".foo
^
Here is it working:
scala> object Bar {
| implicit class Foo(s: String) { def foo = println(s"Foo: $s") }
| }
defined object Bar
scala> import Bar._
import Bar._
scala> "hey".foo
Foo: hey

scala assignment operator _= not working in trait

Here is the code:
trait MacApp {
def dockerIcon_=(s: String) = println("setting docker icon...")
}
object Main extends App with MacApp {
dockerIcon = "apple"
}
The scalac complains this:
Main.scala:6: error: not found: value dockerIcon
dockerIcon = "apple"
^
one error found
I see scala-swing library use _= a lot, e.g., https://github.com/scala/scala-swing/blob/2.0.x/src/main/scala/scala/swing/Label.scala#L28
Thanks!
You need both getter and setter:
scala> :pa
// Entering paste mode (ctrl-D to finish)
trait MacApp {
def dockerIcon_=(s: String) = println("setting docker icon...")
def dockerIcon = 42
}
object Main extends App with MacApp {
dockerIcon = "apple"
}
// Exiting paste mode, now interpreting.
defined trait MacApp
defined object Main
scala> Main main null
setting docker icon...
http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#assignments
Your expectations about the pair of members are established earlier at:
http://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#variable-declarations-and-definitions
You're trying to assign to a function? I don't think that _= Swing code is using an operator, I think it's just an odd name...possibly for some obscure Java compatibility reason that escapes me at the moment.
But def defines a function, not a value, so you can't assign to it; you have to call it. Check out this console fragment:
scala> def text_=(s: String) = s + "foo"
text_$eq: (s: String)String
scala> text_=("bar")
res3: String = barfoo

Using attachments with macros in Scala 2.10

Update: I suspect that what I want might not be possible, and I've written up a blog post with my reasoning (and some alternatives) here. I'd be very happy to be told that I'm wrong.
Suppose I want to create a instance of a trait using a factory method with a macro implementation. This method will take as an argument a path to a resource that the macro will read and parse (at compile time) into a map from strings to strings.
That part is all fairly straightforward. Now suppose I want to associate the resulting map with the instance I'm creating so that I can use it in subsequent macro calls involving that instance.
I crucially do not want the map to be a member of the instance, or to exist in any form at runtime. I also don't want to parse the same resource more than once. Here's a sketch of the kind of thing I'm aiming for:
import scala.language.experimental.macros
import scala.reflect.macros.Context
trait Foo {
def lookup(key: String): String = macro Foo.lookup_impl
}
object Foo {
def parseResource(path: String): Map[String, String] = ???
def apply(path: String): Foo = macro apply_impl
def apply_impl(c: Context)(path: c.Expr[String]): c.Expr[Foo] = {
import c.universe._
val m = path.tree match {
case Literal(Constant(s: String)) => parseResource(s)
}
val tree = reify(new Foo {}).tree
// Somehow associate the map with this tree here.
c.Expr(tree)
}
def lookup_impl(c: Context)(key: c.Expr[String]): c.Expr[String] =
/* Somehow read off the map and look up this key. */ ???
}
This seems to be the sort of thing that attachments are designed to help with (thanks to Eugene Burmako for the pointer), and I've got an attachment-based implementation that allows me to write the following:
Foo("whatever.txt").lookup("x")
Where apply_impl attaches the map to the tree and lookup_impl reads that attachment off the same tree, which it sees as its prefix. Unfortunately this is more or less useless, though, since it doesn't work in the foo.lookup("x") case, where the prefix tree is just the variable foo.
(Note that in my real use case Foo extends Dynamic, and I'm trying to give a macro implementation for selectDynamic instead of lookup, but that shouldn't be relevant here—I'm interested in the general case.)
Is there some way that I can use attachments to get what I want? Is there some other approach that would be more appropriate?
UPDATE I should read the question again after fiddling... :( I just reproduced what you have.
You were quite there I think. This works for me:
object Foo {
// Attachment.get somehow needs a wrapper class.
// Probably because of generics
implicit class LookupMap(m: Map[String, String]) {
def get(s: String) = m.get(s)
}
def parseResource(path: String): LookupMap = Map("test" -> "a")
def apply_impl(c: Context)(path: c.Expr[String]): c.Expr[Foo] = {
import c.universe._
val m = path.tree match {
case Literal(Constant(s: String)) => parseResource(s)
}
val tree = reify(new Foo {}).tree.updateAttachment(m)
c.Expr(tree)
}
def lookup_impl(c: Context { type PrefixType = Foo })
(key: c.Expr[String]): c.Expr[String] = {
import c.universe._
val m = c.prefix.tree.attachments.get[LookupMap].get
val s = key.tree match {
case Literal(Constant(s: String)) => m.get(s).get
}
c.Expr(Literal(Constant(s)))
}
}

Implicit conversion with implicit parameter

I'm implementing a Java interface with a lot of methods with Object parameters, which in my case are really Strings containing user names:
public interface TwoFactorAuthProvider {
boolean requiresTwoFactorAuth(Object principal);
... //many more methods with the same kind of parameter
}
I'm trying to use implicit conversion to convert these to User objects in my implementation:
class TwoFactorAuthProviderImpl(userRepository: UserRepository)
extends TwoFactorAuthProvider {
def requiresTwoFactorAuth(user: User): Boolean = {
...
}
}
When I define the conversion in the companion object of my class, it is picked up just fine and my class compiles:
object TwoFactorAuthProviderImpl {
implicit def toUser(principal: Any): User = {
null //TODO: do something useful
}
}
However, to be able to do the conversion, I need access to the user repository, which the TwoFactorAuthProviderImpl instance has, but the companion object does not. I thought I could possibly use an implicit parameter to pass it:
implicit def toUser(principal: Any)(implicit repo: UserRepository): User = {
val email = principal.asInstanceOf[String]
repo.findByEmail(email)
}
But with the implicit parameter, the conversion is no longer picked up by the compiler (complaining that I'm not implementing the interface).
Is there a way to get the implicit conversion that I want, or is this outside the scope of what you can do with implicits?
This should work just fine - can you supply the exact compilation error? Not implementing what interface? It looks like you would have to declare as follows:
class TwoFactorAuthProviderImpl(implicit userRepository: UserRepository)
Here's an example for the REPL to show that implicits can have implicits; I'm using paste mode to ensure that module X is the companion object of the class X
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class X(i: Int, s: String)
object X { implicit def Int_Is_X(i: Int)(implicit s: String) = X(i, s) }
// Exiting paste mode, now interpreting.
defined class X
defined module X
scala> val i: X = 4
<console>:9: error: value X is not a member of object $iw
val i: X = 4
^
But if we add an implicit string in scope
scala> implicit val s = "Foo"
s: java.lang.String = Foo
scala> val i: X = 4
i: X = X(4,Foo)
Implicits advice
Don't go overboard with implicit conversions - I think you are going too far in this sense - the principal is implicitly a mechanism by which you can discover a user, it is not implicitly a user itself. I'd be tempted to do something like this instead:
implicit def Principal_Is_UserDiscoverable(p: String) = new {
def findUser(implicit repo: UserRepository) = repo.findUser(p)
}
Then you can do "oxbow".findUser
Thanks to Oxbow's answer, I now have it working, this is only for reference.
First of all, a value that should be passed as an implicit must itself be marked implicit:
class TwoFactorAuthProviderImpl(implicit userRepository: UserRepository) ...
Second, implicit conversions are nice and all, but a method implementation signature must still match the signature of its declaration. So this does not compile, even though there is a conversion from Any to User:
def requiresTwoFactorAuth(principal: User): Boolean = { ... }
But leaving the parameter as Any, as in the declaration, and then using it as a user works just fine:
def requiresTwoFactorAuth(principal: Any): Boolean = {
principal.getSettings().getPhoneUsedForTwoFactorAuthentication() != null
}
Also, the conversion really doesn't have to be in the companion object in this case, so in the end, I left the implicit parameters out.
The full source code:
class TwoFactorAuthProviderImpl(userRepository: UserRepository)
extends TwoFactorAuthProvider {
private implicit def toUser(principal: Any): User = {
val email = principal.asInstanceOf[String]
userRepository.findByEmail(email)
}
def requiresTwoFactorAuth(principal: Any): Boolean = {
//using principal as a User
principal.getSettings().getPhoneUsedForTwoFactorAuthentication() != null
}
...
}

Overriding Scala Enumeration Value

As far as I can tell, Scala has definitions for the Enumeration Value class for Value(Int), Value(String), and Value(Int, String).
Does anyone know of an example for creating a new Value subclass to support a different constructor?
For example, If I want to create an Enumeration with Value(Int, String, String) objects, how would I do it? I would like all of the other benefits of using the Enumeration class.
Thanks.
The Enumeration values are instance of the Val class. This class can be extended and a factory method can be added.
object My extends Enumeration {
val A = Value("name", "x")
val B = Value("other", "y")
class MyVal(name: String, val x : String) extends Val(nextId, name)
protected final def Value(name: String, x : String): MyVal = new MyVal(name, x)
}
scala> My.B.id
res0: Int = 1
scala> My.B.x
res1: String = y
Actually in Scala Enumeration has a much simpler meaning than in Java. For your purpose you don't have to subclass Enumeration nor its Value in any way, you just need to instantiate your own type in its companion object as a val. This way you'll get the familiar access model of val value:MyEnum = MyEnum.Value as you had in Java which is not possible in the example provided by Thomas Jung. There you'll have def value:My.MyVal = MyEnum.Value which is kinda confusing as it seems to me besides all the hackiness of the solution. Here's an example of what I propose:
class MyEnum(myParam:String)
object MyEnum {
val Value1 = new MyEnum("any parameters you want")
val Value2 = new MyEnum("")
object Value3 extends MyEnum("A different approach to instantialization which also lets you extend the type in place")
}
Here you'll find a more complicated example: Scala Best Practices: Trait Inheritance vs Enumeration
I would prefer doing it by extending the Enumeration.Val class.
For your requirement, I would post a sample below:
object FileType extends Enumeration {
val csv = Val(1,"csv", ",")
val tsv = Val(2,"tsv", "\t")
protected case class Val(num: Int, fileType: String, delimiter: String) extends super.Val
implicit def valueToFileType(x: Value): Val = x.asInstanceOf[Val]
}
Accessing values is as below:
scala> FileType.csv
res0: FileType.Val = csv
scala> FileType.csv.delimiter
res29: String = ,
Here is another simpler approach:
scala> :paste
// Entering paste mode (ctrl-D to finish)
object Colors extends Enumeration {
sealed case class Color private[Colors](hexCode: String, name: String) extends Val(name)
val Black = Color("#000000", "black")
val White = Color("#FFFFFF", "white")
}
// Exiting paste mode, now interpreting.
defined object Colors
scala> Colors.Black.hexCode
res0: String = #000000
scala> Colors.Black.name
res1: String = black
scala> Colors.values
res2: Colors.ValueSet = Colors.ValueSet(black, white)
scala>