So I'm a total Scala noob and come from a rusty Java background and a primarily PHP+JavaScript one. I'm finding myself getting frustrated, because there are design patterns I want to implement in Scala but I have no idea how, WHich leads to a lot of reading, and never any programming.
Anyway so I am busy making a basic program to learn the ins and outs, and I am making a configuration file reader. What i need to do is map different formats to extensions, the trick being that different extensions such as yaml
and yml
map to the same format.
In java I would use Enums
and control the mapping in the constructor. Pretty straight forward.
In scala ... not so much. First I discover that ADT (meaning Algebraic Data Types not Abstract Data Types) is a better alternative then the use of Enumeration (reasons for which are still not clear to me), and it works better with Java libraries (ironically, and I have no idea why.)
Next I discover the various formats of ADT, and as a beginner grasping all the concepts simultaneously is strenuous. I have managed to figure out that the companion Objects
are similar to javascript in the sense that it is a single instantiation almost like a singleton and allows you to access the defined functions in a static context.
I've written what I thought was a good idea, but the code looks cumbersome, verbose, and I can't figure out some use cases, and It appears from all my JS plugin writing I'm a bit of a namespacing nut that does well for logical separation but not conciseness in scala.
class ConfigManager {
object Formats{
case class Format(f: String){
val format = f.toUpperCase()
}
case object Yaml extends Format("YAML")
case object Xml extends Format("XML")
case object Json extends Format("JSON")
}
object Extensions {
case class Extension(e: String) {
val extension = e.toUpperCase()
}
case object Yaml extends Extension("YAML")
case object Yml extends Extension("YML")
case object Xml extends Extension("XML")
case object Json extends Extension("JSON")
}
object Config {
private sealed trait ConfigFormats {
def f: Formats.Format
def e : Extensions.Extension
}
case class ConfigFormat(f: Formats.Format, e: Extensions.Extension)
extends ConfigFormats (f, e)
case object Yaml1 extends ConfigFormat(Formats.Yaml, Extensions.Yaml)
case object Yaml2 extends ConfigFormat(Formats.Yaml, Extensions.Yml)
case object Xml extends ConfigFormat(Formats.Xml, Extensions.Xml)
case object Json extends ConfigFormat(Formats.Json, Extensions.Json)
}
//this looks nice, and useful, is concise and does what I want
def readConfig(c: Config.ConfigFormat){
c.f match {
case Formats.Yaml => readConfigYaml()
case Formats.Json => readConfigJson()
case Formats.Xml => readConfigXml()
case _ => throw new Exception("Invalid Config format")
}
}
//and this is where I lose the plot
def setFormat(extension: String){
val configFormat = new Extensions.Extension(discoveredFormat) match {
case Config.????? => ????
}
}
}
So the obvious question is how bad exactly is my implementation, and what should I have done, and why? (I'm most concerned about the "Why")
Scala Enums are fine, you could define Formats
and Extensions
like this:
object Formats extends Enumeration {
type Eny = Value
val YAML, XML, JSON = Value
}
object Extensions extends Enumeration {
type Eny = Value
val YAML, YML, XML, JSON = Value
}
And if you want to a map, just use a map:
val configFormats: Map[Extensions.Value, Formats.Value] = Map(
Extensions.YAML -> Formats.YAML,
Extensions.YML -> Formats.YAML,
Extensions.XML -> Formats.XML,
Extensions.JSON -> Formats.JSON)
Then I'm not really sure of what your methods are supposed to do, but it looks like your setFormat
could start like this:
def setFormat(extension: String): Unit = {
val format: Formats.Value = configFormats(Extensions withName extension)
...
}
You see, just like Java :) If you describe better what you are trying to do we might be able to give you a more "idiomatic way".