I have defined two macros:
(defmacro property [name type]
`(setv ^(of Optional ~type) ~name None))
(defmacro data-type [vname &rest propertys]
`(with-decorator dataclass
(defclass ~vname []
~propertys)))
When called as:
(data-type my-test-type
(property name str)
(property unitprice float)
(property qty_on_hand int)
(property test int))
and expanded and translated into python it produces the following:
#dataclass
class my_test_type:
name: Optional[str] = None
unitprice: Optional[float] = None
qty_on_hand: Optional[int] = None
test: Optional[int] = None
[None, None, None, None]
Writing it without the nested macros still yeilds a list of one None:
(data-type my-test-type
(setv ^(of Optional str) name None
^(of Optional float) unitprice None
^(of Optional int) qty_on_hand None
^(of Optional int) test None))
#dataclass
class my_test_type:
name: Optional[str] = None
unitprice: Optional[float] = None
qty_on_hand: Optional[int] = None
test: Optional[int] = None
[None]
Where is this list of [None, None, None, None] coming from? While the list of none won't break anything it's still a little jarring and I wish I knew what would be a better way to write this macro to avoid the list of None.
It looks like you wrote ~propertys but meant ~#propertys. You want to splice each of the property declarations into the defclass, instead of combining them into a list. Making this change removes the trailing list of Nones.
Related
I have a case class like below:
case class Class1(field1: String,
field2: Option[String] = None,
var var1: Option[String] = None,
var var2: Option[Boolean] = None,
var var3: Option[Double] = None
)
The list of variables is a bit longer. Now I want to convert all variables, which are inside the class, into a string. Say Option[] must be omitted and also Boolean, Double and Number must be converted to string type. My first approach was:
def anyOptionalToString(class1Dataset: Dataset[Class1]): DataFrame = {
val ds1 = class1Dataset.map { class1 =>
(
class1.field1,
class1.field2.getOrElse(""),
class1.var1.getOrElse(""),
class1.var2.getOrElse(false),
class1.var3.getOrElse(-1.0)
)
}
Is there a way to cast them without calling every field?
Speak in a kind of loop or something similar?
What I would do, is creating a new Seq containing the defaults you want to have. Let's say:
val defaults = Seq("", "", "", false, -1)
Now, we can use the productIterator to iterate over the existing elements, and choose whether we want to use the existing value, or the default:
val c1 = Class1("f1", Some("f2"), None, Some(true), Some(3))
c1.productIterator.zip(defaults.iterator).map {
case (None, default) => default
case (Some(value), _) => value
case (value, _) => value
}.map(_.toString)
The resulting type of the code above is Iterator[String]. Code run can be found at Scastie.
I have started learning Scala and going through the code (case classes and other stuff) in my project.
I see a case class defined like this:
case class Test(firstName: Option[String] = None, lastName: Option[String])
I have few questions on the above case class:
What is the difference between Option[String] = None and Option[String]?
What is the correct usage, when should we use Option[String] = None and Option[String]?
The "regex" for a parameter is something like this: name: type (= defaultValue) - Note that the default value part is optional.
Thus firstName: Option[String] = None means the parameter is named firstName and it is of type Option[String] and its default value is a None (also remember the Option type is used to explain that a value may exist or not. And that None means it doesn't exist).
Both are correct depending on the context, in this case, this class can be used like:
Test(lastName = None)
// res: Test = Test(None,None) - A person without names.
Test(lastName = Some("Mejia"))
// res: Test = Test(None,Some(Mejia)) - A person with just its last name, which it is "Mejia".
Test(firstName = Some("Luis"), lastName = Some("Mejia"))
// res: Test = Test(Some(Luis),Some(Mejia)) - A person whose first name is "Luis" and its last name is "Mejia".
Test(firstName = Some("Luis"), lastName = None)
// res: Test = Test(Some(Luis),None) - A person with just its first name, which it is "Luis".
Note that I always have to specify its last name because it doesn't have a default value.
I have defined:
final case class EventOpt(start: Option[Long], end: Option[Long])
final case class Event(start: Long, end: Long)
The only interesting objects for me are those with both fields set and I want to filter the rest
Having List[EventOpt] I'd like to convert it to List[Option[Event]] using Some(Event) when both start and end are set and None when either of start and end them is None?
Eg.
List(EventOpt(Some(1), None), EventOpt(None, Some(2)), EventOpt(Some(3), Some(4)))
=>
List(None, None, Some(Event(4, 3)))
By doing so, it will allow be to do a simple flatMap over the last list and omit the Nones
You don't need a separate flatMap step, simply collect only what you need right away:
eventOpts.collect { case EventOpt(Some(x), Some(y)) => Event(x, y) }
This works:
scala> def test(name: String = "joe"): Boolean = true
test: (name: String)Boolean
I expected this to work in the same way:
scala> val test: String => Boolean = { (name: String = "joe") => true }
console>:1: error: ')' expected but '=' found.
The boring, correct answer is no, you can't, but actually you kind of can, with the experimental single abstract method (SAM) synthesis in 2.11.
First you need to define your own SAM type with the default value for the apply method's parameter:
trait Func {
def apply(name: String = "joe"): Boolean
}
Now you can use the function literal notation to define a Func (note that you'll need to have started the REPL with -Xexperimental for this step to work):
val f: Func = { (name: String) => name == "joe" }
Or just:
val f: Func = _ == "joe"
And then the usual stuff:
scala> f("joe")
res0: Boolean = true
scala> f("eoj")
res1: Boolean = false
And the punchline:
scala> f()
res2: Boolean = true
It's not exactly the syntax you're asking for, and there are no promises that this will ever leave experimental status, and even if it does, default arguments may not be supported—but I still think it's pretty neat that it works now.
To expand on "The boring, correct answer is no" in Travis Brown's answer:
Functions (i.e. objects of FunctionN[...] type) can't have default arguments in Scala, only methods can. Since anonymous function expressions produce functions (and not methods), they can't have default arguments.
This is bit dirty solution I think, using currying
def testDef(nameDef: String)(name2: String): Boolean = {
val name3 = if ( name2 != "") name2 else nameDef
//use name3 for your logic
true
}
//> testDef: (name: String)(name2: String)Boolean
Curry the testDef method to hold default value as shown below.
var test = test("joe")_ //> test : String => Boolean=function1
test("fun") //"fun" will be used as name3
test("") // "joe" will be used as name3
I working with play for Scala (2.1) and I need to convert an Option[Long] value to Long.
I know how to do the opposite, I mean:
def toOption[Long](value: Long): Option[Long] = if (value == null) None else Some(value)
But in my case, I have to pass a value of Option[Long] as a type into a method that takes Long.
If you have x as Option[Long], x.get will give you Long.
First of all, your implementation of "the opposite" has some serious problems. By putting a type parameter named Long on the method you're shadowing the Long type from the standard library. You probably mean the following instead:
def toOption(value: Long): Option[Long] =
if (value == null) None else Some(value)
Even this is kind of nonsensical (since scala.Long is not a reference type and can never be null), unless you're referring to java.lang.Long, which is a recipe for pain and confusion. Finally, even if you were dealing with a reference type (like String), you'd be better off writing the following, which is exactly equivalent:
def toOption(value: String): Option[String] = Option(value)
This method will return None if and only if value is null.
To address your question, suppose we have the following method:
def foo(x: Long) = x * 2
You shouldn't generally think in terms of passing an Option[Long] to foo, but rather of "lifting" foo into the Option via map:
scala> val x: Option[Long] = Some(100L)
x: Option[Long] = Some(100)
scala> x map foo
res14: Option[Long] = Some(200)
The whole point of Option is to model (at the type level) the possibility of a "null" value in order to avoid a whole class of NullPointerException-y problems. Using map on the Option allows you to perform computations on the value that may be in the Option while continuing to model the possibility that it's empty.
As another answer notes, it's also possible to use getOrElse to "bail out" of the Option, but this usually isn't the idiomatic approach in Scala (except in cases where there really is a reasonable default value).
This method is already defined on Option[A] and is called get :
scala> val x = Some(99L)
x: Some[Long] = Some(99)
scala> x.get
res0: Long = 99
The problem is that calling get on None will throw a NoSucheElement Exception:
scala> None.get
java.util.NoSuchElementException: None.get
thus you will not gain any benefits from using an Option type.
Thus as stated before you can use getOrElse if you can provide a sensible default value or handle the Exception.
The idiomatic scala way would be using map or a for-comprehension
x map (_ + 1)
res2: Option[Long] = Some(100)
or
for (i <- x) yield i +1
res3: Option[Long] = Some(100)
Option is way to localise side-effect (your function can return empty value). And good style to lift your computation to Option (Option is Monad with map & flatMap methods).
val x = Option[Long](10)
x.map { a => a + 10 }
And extract value with manually processing of side effect:
val res = x match {
case Some(a) => s"Value: $a"
case None => "no value"
}
You need to decide what happens when the option is None. Do you provide a default value?
def unroll(opt: Option[Long]): Long = opt getOrElse -1L // -1 if undefined
unroll(None) // -> -1
You could also throw an exception:
def unroll(opt: Option[Long]): Long = opt.getOrElse(throw
new IllegalArgumentException("The option is expected to be defined at this point")
)
unroll(None) // -> exception
In case, refrain from using null, unless you have very good reasons to use it (opt.orNull).
As has already been mentioned getOrElse is probably what you're looking for in answering your question directly.
Please note also that to convert to an option you can simply:
val myOption = Option(1)
myOption will now be Some(1)
val myOption = Option(null)
myOption will now be None.