What is the "Value" in Scala Enum class? - scala

I am new to scala and I am reading the Enum class in this page(https://www.scala-lang.org/api/2.13.3/scala/Enumeration.html)
I have two questions:
In the definition of WeekDay, what is the "Value" in val Mon, Tue, Wed, Thu, Fri, Sat, Sun = "Value"
After the definition of object WeekDay, why we need "import WeekDay._" here ?
Thank you!
Screenshot of the code is here

From scala doc
Each call to a Value method adds a new unique value to the enumeration. To be accessible, these values are usually defined as val members of the enumeration.
So Mon, Tue, Wed, Thu, Fri, Sat, Sun are just simply values, each instantiated by calling a function called Value (see https://github.com/scala/scala/blob/2.13.x/src/library/scala/Enumeration.scala#L162)
Importing is not for defining the Weekdays but for the usage. Notice that Mon, Tue, etc is defined inside the object. Just like every identifiers in scala, you need to import to access identifiers defined in another package or object. It'll be clearer if you define the enum in 1 file and use it another file
You didn't ask but in many cases you should also consider using sealed trait or plain java enum so that you get exhaustive matching warning. It's a very useful thing that scala compiler tells you when you don't handle all the cases in enumeration. Many people consider that an error in modeling if you can't handle all the cases. See https://underscore.io/blog/posts/2014/09/03/enumerations.html

Related

How to chain more than 23 elements in a tuple in Scala Cats?

In Scala I am making use of the Cats Library and I am using the mapN() function. Lets say I have created the following case class with 22 elements. The following code will work without any problem.
case class Example1 (element1: String , element2: String ..... element22)
('string1', ...'string22').mapN(Example1.apply)
Lets say I want a element23. But unfortunately the compiler will complain that there are arguments missing because the mapping is not done correctly. Is there any way of chaining the 23rd element because I saw that there is a tuple of max 22 elements handled by the mapN() function.
To my knowledge, there is no support for mapN for tuples of arity > 22. One could probably change that in cats by fiddling around with maxArity here (though that would require doing that for Scala 3 only).
However: for your specific use case (converting tuples to a case class), Scala 3 (which I assume you are using because Scala 2 does not support tuples of arity > 22) would offer a simple solution: see Converting between tuples and case classes in Scala 3 and this runnable scastie example.

Add duration to a moment using time4j

As I am converting code using the java.time implementation to time4j, I want to add a Duration to a Moment but I get a compilation error. Under java.time, I would do the following:
val startTime: ZonedDateTime = ...
val duration: TemporalAmount = ...
val endTime: ZonedDateTime = startTime.plus(duration)
Using time4j though, the same does not work:
val startTime: Moment = ...
val duration: Duration[ClockUnit] = ...
val endTime: Moment = startTime.plus(duration)
The compiler complains about the generics interaction between the Duration and the Moment. No matter which way I create a Duration (at least that I found), it would need to have an associated generic of java.util.concurrent.TimeUnit, because Moment implements TimePoint[java.util.concurrent.TimeUnit] and the Moment#plus method hence needs a Duration[java.util.concurrent.TimeUnit] with the same associated generic as Moment for the time unit.
This surprises me that java.util.concurrent.TimeUnit is used as this is not a time4j type. Am I missing the finer details around this choice? I have the impression that this was decided by design.
One way that works is if I use a PlainTimestamp and adds a Duration[ClockUnit | CalendarUnit | IsoUnit], as the former type implements TimePoint[IsoUnit, PlainTime] and overloads extra supported units. Then I can convert the PlainTimestamp to whatever timezone I need afterward. Is this the intended design?
(And still:) What's the proper Duration type to use with Moment.plus method?
I am using time4j version 4.27.2
Short answer how to migrate zonedDateTime.plus(Duration.ofHours(24)):
Moment.from(zonedDateTime.toInstant()).plus(MachineTime.of(24, TimeUnit.HOURS));
See also the API of the global duration type MachineTime.
Long answer in detail:
In contrast to the java.time-API which only knows only one single class ChronoUnit for representing the most used temporal units applicable on arbitrary temporal entities (and the general interface TemporalUnit), the unit and duration design of the library Time4J is much more fine-grained.
Every calendar or temporal type with a timeline has its own unit type which also characterizes the timeline of the temporal type in a distinct way so even the compiler will tell you not to mix up different unit types of different entities like Moment (counterpart to java.time.Instant) or PlainTimestamp (counterpart to LocalDateTime).
This distinction is coded by design as generic type parameter U in the super class TimePoint. For concrete classes: Moment works primarily with java.util.concurrent.TimeUnit while PlainTimestamp works with any implementation of IsoUnit, especially with the enums CalendarUnit and ClockUnit.
The generalized duration concept is described by the interface TimeSpan which is also specified by the unit type U to be used. You can only add a timespan to a moment if the unit type is java.util.concurrent.TimeUnit. This is offered by the special implementation class MachineTime. Furthermore, the other duration implementation, called net.time4j.Duration is only compatible with local types like PlainTimestamp.
Special temporal entities have their own sets of suitable units. For example, the unit for eras does only exist in the Japanese calendar but not for other calendars which have either zero, one or only two eras.
Why different unit types for Moment and PlainTimestamp?
Partially this is already answered by the last item about suitable sets of units. Example, months and years do not make much sense for machine-like types as Moment. Therefore the chosen enum java.util.concurrent.TimeUnit covers what is needed as units for Moment.
In addition, the different unit types of Time4J help to differentiate. A net.time4j.Duration<ClockUnit> is calculated in a local context while a MachineTime<TimeUnit> is calculated as global duration. This is not only true for clock-related units like hours but also for calendrical units. A year is not simply a year. We have ISO-calendar years (corresponding to gregorian years). We have ISO-week-based-years (length of 364 or 371 days). We have islamic years (354 or 355 days) and so on. Therefore Time4J knows a lot of different calendar units (watch out the API of the calendar-module). So Time4J finally adopted a design to prevent comparisons of durations with different unit types (which would be like comparing apples and oranges).
Following example for the rare case of changing the international dateline in Samoa (2011-12-30 was left out) demonstrates how important the distinction of unit types might be:
In Time4J, we just use different unit types to express if the arithmetic happens on the local or on the global timeline. Conclusion: In Java-8, we have to carefully study the context, in Time4J the unit types give valuable extra information.
ZonedDateTime zdt = ZonedDateTime.of(2011, 12, 29, 0, 0, 0, 0, "Pacific/Apia");
Moment m1 = Moment.from(zdt.toInstant());
Moment m2 = m1.plus(MachineTime.of(24, TimeUnit.HOURS));
assertThat(m2.isSimultaneous(m1.plus(MachineTime.of(1, TimeUnit.DAYS))), is(true));
System.out.println(m2); // 2011-12-30T10:00:00Z
System.out.println(m2.toZonalTimestamp(PACIFIC.APIA)); // 2011-12-31T00
System.out.println(m1.toZonalTimestamp(PACIFIC.APIA).plus(2, CalendarUnit.DAYS)); // 2011-12-31T00
The objects TimeUnit.DAYS and CalendarUnit.DAYS are obviously not the same. They require even different amounts (1 versus 2) to create the same results.
Side note:
I have now shortened the first version of my answer - mainly leaving out the Java-8-related stuff because I think it is easily possible to write too much about the topic of unit/duration-design in the narrow context of your question (and I have not even given any kind of complete answer). A tutorial page or extra documentation page like here on SO would indeed be a better place.
But at least two other points not mentioned in my answer might be interesting for you, too (concerns net.time4j.Duration): Sign handling and specialized timezone-metric. Last one can even be an alternative for MachineTime in some cases.

Matlab Enumeration error

I want to use an enumeration in Matlab. I follow a simlpy example as follow:
I created a classdef with the following enumeration and save it in a "WeekDays.m" file.
classdef WeekDays
enumeration
Monday, Tuesday, Wednesday, Thursday, Friday
end
end
Then I tried to access its value from command window (still in the same directory where the "WeekDays.m" is located):
day = WeekDays.Monday;
However when I run the command, i received this error: "Attempt to define enumeration without Enumeration class attribute in class 'WeekDays' ".
How to use Enumeration correctly ? (By the way I'm using Matlab 2010)
Thanks

OCL define invariant

If I have to define an invariant to state that the age of a person requesting a loan should be greater than 18 for the attached class diagram would this be
context Person
inv: age > 18
or
context Person
inv: self.age > 18
and what is the difference between the two?
Thank you for your guidance
Both invariants in your example are exactly the same (the "self" is implicit in the first). Nevertheless, there you just say that all "person" objects must have an age value of at least 18 which is not exactly the invariant you described (in your invariant, it seemed that it was possible to have people under 18 in the system, it´s just that people under 18 could not be linked to a loan)
As Jordi already said they are semantically equivalent. The first includes the self implicitly.
As he mentioned either, your OCL invariant does not match the textual representation you gave us. In your invariant, people must be older than 18. However, the owner of the load should be greater than 18. So your invariant should be formulated on Loan.
context Loan
inv: owner.age > 18
which also has implicit self, so the second is semantically equivalent to the first one:
context Loan
inv: self.owner.age > 18

Scala equivalent of flags

Isn't there an equivalent for flags in Scala ?
Something like :
val flags = Something | SomeOtherThing
I guess I could do a class for each flag set and have a bunch of booleans but what about syntactic sugar?
It largely depends on what you're trying to accomplish.
If you're interfacing with legacy code (especially one that you cannot modify) and/or need super-extra-high efficiency (you usually don't), a canonical way is to create a type alias for Int. I.e., as in the linked library example:
type MyFlags = Int
...
val theseFlags: MyFlags
The advantage over merely using Int is that you can "mark" any usage of those flags with the type alias - this can help immensely with code readability and help avoid bugs due to "mixing up" flags.
For this approach, within Scala-specific code, you can assist yourself by (ab)using BitSet (more info here).
If you want to create a more "object-friendly" implementation, the solution is similar to Java's enum/EnumSet combo - namely, use Enumeration with ValueSet . An example adapted from the library doc:
scala> object WeekDay extends Enumeration {
| type WeekDay = Value
| val Mon, Tue, Wed, Thu, Fri, Sat, Sun = WeekDay
| }
defined module WeekDay
scala> import WeekDay._
import WeekDay._
scala> var newSet = ValueSet.empty
newSet: WeekDay.ValueSet = WeekDay.ValueSet()
scala> newSet += Mon
scala> newSet
res1: WeekDay.ValueSet = WeekDay.ValueSet(Mon)
scala> newSet & WeekDay.values
res2: WeekDay.ValueSet = WeekDay.ValueSet(Mon)
A problem with ValueSet and Value arises - their lack of type-parametrization allows for mixing two different types of flags. Of course, this is only a deficiency when compared to Java's EnumSet. In the context of Scala, the workaround is exactly the same as in the Int example - use a type alias, this time for Value (as shown above).
And as an added bonus, all your flags have unique names.
As a pro-forma, I should also mention Value Classes. Together with a type alias, they would allow you to add custom operators to your flags while preserving the efficiency of Ints.
However, that's likely an overkill for most usage scenarios - you're usually better off using the second, Enumeration-based approach. It should be sufficiently efficient for most cases, especially since ValueSet is really a thin wrapper around BitSet.
If your Something or SomeOtherThing are numbers, this should work.
If you want to make it more obvious, try specifying the type:
val flags : Int = Something | SomeOtherThing
Bitwise operators should work in Scala pretty much like they work in Java. I've found this brief explanation on Google, which you might find helpful.
You may run into a lot of confusing error messages about conflicts between Weekday.type and Weekday.Value if you don't update one line from the mikolak's post.
Update
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = WeekDay
to
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value