The preferred approach would be to use something similar to the commented out line below.
def main(args: Array[String]) {
// val (dbPropsFile, tsvFile, dbTable) = args
val dbPropsFile = args(0)
val tsvFile = args(1)
val dbTable = args(2)
However I am having a little quarrel with the compiler over it:
Error:(13, 9) constructor cannot be instantiated to expected type;
found : (T1, T2, T3)
required: Array[String]
val (dbPropsFile, tsvFile, dbTable) = args
^
So all told this should be an easy few points for someone out there.
Use
val Array(dbPropsFile, tsvFile, dbTable) = args
scala> val Array(a,b,c) = Array(1,2,3)
a: Int = 1
b: Int = 2
c: Int = 3
scala> a
res0: Int = 1
Related
I use scala f string interpolator as follows:
def format(id: Int) = f"A$id%04d"
format(21) // A0021
However, I would like to be able to define a length once and for all (before fixed to 4), and get a function that it is going to format the string with that length.
So, instead of having
def format(length: Int, id: Int) = ???
f(5, 21) // A00021
I would like to have this:
def format(length: Int)(id: Int) = ???
val f = format(5)
f(21) // A00021
How can I implement this using scala f interpolator or other?
Update
I was not looking for such a solution involving the compiler at runtime, but I appreciate som-snytt's answer. Here there is a working solution based on his answer:
import scala.tools.reflect._,scala.reflect.runtime._,universe._
def defFormat(length: Int): Int => String = {
val code = raw"""(i: Int) => f"A$$i%0${length}d""""
tb.eval(tb.parse(code)).asInstanceOf[Int => String]
}
val format = defFormat(length = 5)
format(21)
scala> def format(n: Int)(i: Int) =
| f"A%%0${n}d" format i
format: (n: Int)(i: Int)String
scala> format(5) _
res0: Int => String = <function1>
scala> .apply(21)
res1: String = A00021
Edit:
scala> import scala.tools.reflect._,scala.reflect.runtime._,universe._
import scala.tools.reflect._
import scala.reflect.runtime._
import universe._
scala> val tb = currentMirror.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl#2d10e0b1
scala> def f(n: Int)(i: Int): String = {
| val code = raw"""f"A$${$i}%0${n}d""""
| tb.eval(tb.parse(code)).asInstanceOf[String]
| }
f: (n: Int)(i: Int)String
scala> val g = f(5) _
g: Int => String = <function1>
scala> g(21)
res9: String = A00021
That doesn't actually help much. You really want to
scala> tb.typecheck(tb.parse(code))
scala.tools.reflect.ToolBoxError: reflective typecheck has failed: illegal conversion character 'k'
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$typecheck$1.apply(ToolBoxFactory.scala:178)
which throws if the format is bad.
scala> val code = raw"""(i: Int) => f"A$${i}%k0${10}d""""
code: String = (i: Int) => f"A${i}%k010d"
scala> tb.typecheck(tb.parse(code))
scala.tools.reflect.ToolBoxError: reflective typecheck has failed: illegal conversion character 'k'
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$typecheck$1.apply(ToolBoxFactory.scala:178)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$typecheck$1.apply(ToolBoxFactory.scala:170)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$11.apply(ToolBoxFactory.scala:148)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$11.apply(ToolBoxFactory.scala:148)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$9.apply(ToolBoxFactory.scala:138)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$9.apply(ToolBoxFactory.scala:138)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$withContext$1$1.apply(ToolBoxFactory.scala:139)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$withContext$1$1.apply(ToolBoxFactory.scala:139)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$7.apply(ToolBoxFactory.scala:137)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$7.apply(ToolBoxFactory.scala:137)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1.apply(ToolBoxFactory.scala:148)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1.apply(ToolBoxFactory.scala:121)
at scala.reflect.internal.Trees$class.wrappingIntoTerm(Trees.scala:1716)
at scala.reflect.internal.SymbolTable.wrappingIntoTerm(SymbolTable.scala:16)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.withWrapping$1(ToolBoxFactory.scala:120)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.transformDuringTyper(ToolBoxFactory.scala:121)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.typecheck(ToolBoxFactory.scala:169)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$$anonfun$typecheck$2.apply(ToolBoxFactory.scala:375)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$$anonfun$typecheck$2.apply(ToolBoxFactory.scala:367)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.liftedTree2$1(ToolBoxFactory.scala:355)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.apply(ToolBoxFactory.scala:355)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.typecheck(ToolBoxFactory.scala:367)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.typecheck(ToolBoxFactory.scala:27)
... 32 elided
scala> val code = raw"""(i: Int) => f"A$${i}%0${10}d""""
code: String = (i: Int) => f"A${i}%010d"
scala> tb.typecheck(tb.parse(code))
res19: tb.u.Tree =
((i: Int) => ({
val arg$macro$9: Int = i;
new scala.collection.immutable.StringOps("A%010d").format(arg$macro$9)
}: String))
You can't do it using f because its whole point is to make sure it can check the format string for type errors, so the format string has to be static. f could support this scenario explicitly, but it doesn't.
You could make format a macro, but this seems like an overkill. Not to mention that it would have to be defined in a separate module, which looks very inconvenient for this scenario.
How do I avoid the initialization (lines 5 and 6) here?
import scala.collection._
def newHash = mutable.Map[String,String]()
def newHoH = mutable.Map[String,mutable.Map[String,String]]()
var foo = mutable.Map[String,mutable.Map[String,mutable.Map[String,String]]]()
foo("bar") = newHoH //line 5
foo("bar")("baz") = newHash //line 6
foo("bar")("baz")("whee") = "duh"
I tried withDefaultValue with a simpler example but obviously I did it wrong:
/***
scala> var foo = mutable.Map[String,mutable.Map[String,String]]().withDefaultValue(mutable.Map(""->""))
foo: scala.collection.mutable.Map[String,scala.collection.mutable.Map[String,String]] = Map()
scala> foo("bar")("baz") = "duh"
scala> foo("b")("baz") = "der"
scala> foo("bar")("baz")
res7: String = der
*/
The withDefault method won't work here. A Map created this way returns a new map everytime there is no key, so calling mymap("foo")("bar") = "ok" will assign "ok" into a temporarily created map, but the next time you call mymap("foo")("bar"), a non-existent "foo" key on mymap will result in creating a new map, which will not contain the mapping "foo" -> "bar".
Instead, consider creating an anonymous map. I show a solution with only 1 nestings:
‡ scala-version 2.10.1
Welcome to Scala version 2.10.1 (Java HotSpot(TM) Server VM, Java 1.7.0_21).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import collection._
import collection._
scala> :paste
// Entering paste mode (ctrl-D to finish)
def newHash = mutable.Map[String,String]().withDefault(_ => "")
def newHoH = new mutable.Map[String,mutable.Map[String,String]]() {
val m = mutable.Map[String, mutable.Map[String, String]]()
def +=(kv: (String, mutable.Map[String, String])) = { m += kv; this }
def -=(k: String) = { m -= k; this }
def get(k: String) = m.get(k) match {
case opt # Some(v) => opt
case None =>
val v = newHash
m(k) = v
Some(v)
}
def iterator = m.iterator
}
// Exiting paste mode, now interpreting.
newHash: scala.collection.mutable.Map[String,String]
newHoH: scala.collection.mutable.Map[String,scala.collection.mutable.Map[String,String]]{val m: scala.collection.mutable.Map[String,scala.collection.mutable.Map[String,String]]}
scala> val m = newHoH
m: scala.collection.mutable.Map[String,scala.collection.mutable.Map[String,String]]{val m: scala.collection.mutable.Map[String,scala.collection.mutable.Map[String,String]]} = Map()
scala> m("foo")("bar") = "ok"
scala> m("foo")("bar")
res1: String = ok
I'm looking to create a class that is basically a collection with an extra field. However, I keep running into problems and am wondering what the best way of implementing this is. I've tried to follow the pattern given in the Scala book. E.g.
import scala.collection.IndexedSeqLike
import scala.collection.mutable.Builder
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.ArrayBuffer
class FieldSequence[FT,ST](val field: FT, seq: IndexedSeq[ST] = Vector())
extends IndexedSeq[ST] with IndexedSeqLike[ST,FieldSequence[FT,ST]] {
def apply(index: Int): ST = return seq(index)
def length = seq.length
override def newBuilder: Builder[ST,FieldSequence[FT,ST]]
= FieldSequence.newBuilder[FT,ST](field)
}
object FieldSequence {
def fromSeq[FT,ST](field: FT)(buf: IndexedSeq[ST])
= new FieldSequence(field, buf)
def newBuilder[FT,ST](field: FT): Builder[ST,FieldSequence[FT,ST]]
= new ArrayBuffer mapResult(fromSeq(field))
implicit def canBuildFrom[FT,ST]:
CanBuildFrom[FieldSequence[FT,ST], ST, FieldSequence[FT,ST]] =
new CanBuildFrom[FieldSequence[FT,ST], ST, FieldSequence[FT,ST]] {
def apply(): Builder[ST,FieldSequence[FT,ST]]
= newBuilder[FT,ST]( _ ) // What goes here?
def apply(from: FieldSequence[FT,ST]): Builder[ST,FieldSequence[FT,ST]]
= from.newBuilder
}
}
The problem is the CanBuildFrom that is implicitly defined needs an apply method with no arguments. But in these circumstances this method is meaningless, as a field (of type FT) is needed to construct a FieldSequence. In fact, it should be impossible to construct a FieldSequence, simply from a sequence of type ST. Is the best I can do to throw an exception here?
Then your class doesn't fulfill the requirements to be a Seq, and methods like flatMap (and hence for-comprehensions) can't work for it.
I'm not sure I agree with Landei about flatMap and map. If you replace with throwing an exception like this, most of the operations should work.
def apply(): Builder[ST,FieldSequence[FT,ST]] = sys.error("unsupported")
From what I can see in TraversableLike, map and flatMap and most other ones use the apply(repr) version. So for comprehensions seemingly work. It also feels like it should follow the Monad laws (the field is just carried accross).
Given the code you have, you can do this:
scala> val fs = FieldSequence.fromSeq("str")(Vector(1,2))
fs: FieldSequence[java.lang.String,Int] = FieldSequence(1, 2)
scala> fs.map(1 + _)
res3: FieldSequence[java.lang.String,Int] = FieldSequence(2, 3)
scala> val fs2 = FieldSequence.fromSeq("str1")(Vector(10,20))
fs2: FieldSequence[java.lang.String,Int] = FieldSequence(10, 20)
scala> for (x <- fs if x > 0; y <- fs2) yield (x + y)
res5: FieldSequence[java.lang.String,Int] = FieldSequence(11, 21, 12, 22)
What doesn't work is the following:
scala> fs.map(_ + "!")
// does not return a FieldSequence
scala> List(1,2).map(1 + _)(collection.breakOut): FieldSequence[String, Int]
java.lang.RuntimeException: unsupported
// this is where the apply() is used
For breakOut to work you would need to implement the apply() method. I suspect you could generate a builder with some default value for field: def apply() = newBuilder[FT, ST](getDefault) with some implementation of getDefault that makes sense for your use case.
For the fact that fs.map(_ + "!") does not preserve the type, you need to modify your signature and implementation, so that the compiler can find a CanBuildFrom[FieldSequence[String, Int], String, FieldSequence[String, String]]
implicit def canBuildFrom[FT,ST_FROM,ST]:
CanBuildFrom[FieldSequence[FT,ST_FROM], ST, FieldSequence[FT,ST]] =
new CanBuildFrom[FieldSequence[FT,ST_FROM], ST, FieldSequence[FT,ST]] {
def apply(): Builder[ST,FieldSequence[FT,ST]]
= sys.error("unsupported")
def apply(from: FieldSequence[FT,ST_FROM]): Builder[ST,FieldSequence[FT,ST]]
= newBuilder[FT, ST](from.field)
}
In the end, my answer was very similar to that in a previous question. The difference with that question and my original and the answer are slight but basically allow anything that has a sequence to be a sequence.
import scala.collection.SeqLike
import scala.collection.mutable.Builder
import scala.collection.mutable.ArrayBuffer
import scala.collection.generic.CanBuildFrom
trait SeqAdapter[+A, Repr[+X] <: SeqAdapter[X,Repr]]
extends Seq[A] with SeqLike[A,Repr[A]] {
val underlyingSeq: Seq[A]
def create[B](seq: Seq[B]): Repr[B]
def apply(index: Int) = underlyingSeq(index)
def length = underlyingSeq.length
def iterator = underlyingSeq.iterator
override protected[this] def newBuilder: Builder[A,Repr[A]] = {
val sac = new SeqAdapterCompanion[Repr] {
def createDefault[B](seq: Seq[B]) = create(seq)
}
sac.newBuilder(create)
}
}
trait SeqAdapterCompanion[Repr[+X] <: SeqAdapter[X,Repr]] {
def createDefault[A](seq: Seq[A]): Repr[A]
def fromSeq[A](creator: (Seq[A]) => Repr[A])(seq: Seq[A]) = creator(seq)
def newBuilder[A](creator: (Seq[A]) => Repr[A]): Builder[A,Repr[A]] =
new ArrayBuffer mapResult fromSeq(creator)
implicit def canBuildFrom[A,B]: CanBuildFrom[Repr[A],B,Repr[B]] =
new CanBuildFrom[Repr[A],B,Repr[B]] {
def apply(): Builder[B,Repr[B]] = newBuilder(createDefault)
def apply(from: Repr[A]) = newBuilder(from.create)
}
}
This fixes all the problems huynhjl brought up. For my original problem, to have a field and a sequence treated as a sequence, a simple class will now do.
trait Field[FT] {
val defaultValue: FT
class FieldSeq[+ST](val field: FT, val underlyingSeq: Seq[ST] = Vector())
extends SeqAdapter[ST,FieldSeq] {
def create[B](seq: Seq[B]) = new FieldSeq[B](field, seq)
}
object FieldSeq extends SeqAdapterCompanion[FieldSeq] {
def createDefault[A](seq: Seq[A]): FieldSeq[A] =
new FieldSeq[A](defaultValue, seq)
override implicit def canBuildFrom[A,B] = super.canBuildFrom[A,B]
}
}
This can be tested as so:
val StringField = new Field[String] { val defaultValue = "Default Value" }
StringField: java.lang.Object with Field[String] = $anon$1#57f5de73
val fs = new StringField.FieldSeq[Int]("str", Vector(1,2))
val fsfield = fs.field
fs: StringField.FieldSeq[Int] = (1, 2)
fsfield: String = str
val fm = fs.map(1 + _)
val fmfield = fm.field
fm: StringField.FieldSeq[Int] = (2, 3)
fmfield: String = str
val fs2 = new StringField.FieldSeq[Int]("str1", Vector(10, 20))
val fs2field = fs2.field
fs2: StringField.FieldSeq[Int] = (10, 20)
fs2field: String = str1
val ffor = for (x <- fs if x > 0; y <- fs2) yield (x + y)
val fforfield = ffor.field
ffor: StringField.FieldSeq[Int] = (11, 21, 12, 22)
fforfield: String = str
val smap = fs.map(_ + "!")
val smapfield = smap.field
smap: StringField.FieldSeq[String] = (1!, 2!)
smapfield: String = str
val break = List(1,2).map(1 + _)(collection.breakOut): StringField.FieldSeq[Int]
val breakfield = break.field
break: StringField.FieldSeq[Int] = (2, 3)
breakfield: String = Default Value
val x: StringField.FieldSeq[Any] = fs
val xfield = x.field
x: StringField.FieldSeq[Any] = (1, 2)
xfield: String = str
I want to have class that can be instantiated with list, array, seq, set, stack, queue etc.
In my opinion
class A
class B(elems:A*)
should handle such stuff.
This is my solution:
class A
class B(elems:Iterable[A]){
def this(elem:A) = this(Seq(elem))
}
Can you suggest any improvements?
Any Seq or Array may be passed to a method with repeated parameters by using the : _* ascription:
scala> def m1(strs: String*): Int = { strs.foldLeft(0)(_ + _.length) }
m1: (strs: String*)Int
scala> m1("foo", "bar")
res0: Int = 6
scala> val ss1 = Array("hello", ", ", "world", ".")
ss1: Array[java.lang.String] = Array(hello, , , world, .)
scala> m1(ss1: _*)
res1: Int = 13
scala> val ss2 = List("now", "is", "the", "time")
ss2: List[java.lang.String] = List(now, is, the, time)
scala> m1(ss2: _*)
res2: Int = 12
This might be a minor improvement.
class A
class B(elems:Iterable[A]){
def this(elem:A*) = this(elem.asInstanceOf[Iterable[A]])
}
That'll make these legal
val b1 = new B(a1)
val b2 = new B(a2, a3)
What is wrong is the following method?
def someMethod(funcs: => Option[String]*) = {
...
}
That actually "works" under 2.7.7 if you add parens:
scala> def someMethod(funcs: => (Option[String]*)) = funcs
someMethod: (=> Option[String]*)Option[String]*
except it doesn't actually work at runtime:
scala> someMethod(Some("Fish"),None)
scala.MatchError: Some(Fish)
at scala.runtime.ScalaRunTime$.boxArray(ScalaRunTime.scala:136)
at .someMethod(<console>:4)
at .<init>(<console>:6)
at .<clinit>(<console>) ...
In 2.8 it refuses to let you specify X* as the output of any function or by-name parameter, even though you can specify it as an input (this is r21230, post-Beta 1):
scala> var f: (Option[Int]*) => Int = _
f: (Option[Int]*) => Int = null
scala> var f: (Option[Int]*) => (Option[Int]*) = _
<console>:1: error: no * parameter type allowed here
var f: (Option[Int]*) => (Option[Int]*) = _
But if you try to convert from a method, it works:
scala> def m(oi: Option[Int]*) = oi
m: (oi: Option[Int]*)Option[Int]*
scala> var f = (m _)
f: (Option[Int]*) => Option[Int]* = <function1>
scala> f(Some(1),None)
res0: Option[Int]* = WrappedArray(Some(1), None)
So it's not entirely consistent.
In any case, you can possibly achieve what you want by passing in an Array and then sending that array to something that takes repeated arguments:
scala> def aMethod(os: Option[String]*) { os.foreach(println) }
aMethod: (os: Option[String]*)Unit
scala> def someMethod(funcs: => Array[Option[String]]) { aMethod(funcs:_*) }
someMethod: (funcs: => Array[Option[String]])Unit
scala> someMethod(Array(Some("Hello"),Some("there"),None))
Some(Hello)
Some(there)
None
If you really want to (easily) pass a bunch of lazily evaluated arguments, then you need a little bit of infrastructure that as far as I know doesn't nicely exist in the library (this is code for 2.8; view it as inspiration for a similar strategy in 2.7):
class Lazy[+T](t: () => T, lt: Lazy[T]) {
val params: List[() => T] = (if (lt eq null) Nil else t :: lt.params)
def ~[S >: T](s: => S) = new Lazy[S](s _,this)
}
object Lz extends Lazy[Nothing](null,null) {
implicit def lazy2params[T : Manifest](lz: Lazy[T]) = lz.params.reverse.toArray
}
Now you can easily create a bunch of parameters that are lazily evaluated:
scala> import Lz._ // To get implicit def
import Lz._
scala> def lazyAdder(ff: Array[()=>Int]) = {
| println("I'm adding now!");
| (0 /: ff){(n,f) => n+f()}
| }
lazyAdder: (ff: Array[() => Int])Int
scala> def yelp = { println("You evaluated me!"); 5 }
yelp: Int
scala> val a = 3
a: Int = 3
scala> var b = 7
b: Int = 7
scala> lazyAdder( Lz ~ yelp ~ (a+b) )
I'm adding now!
You evaluated me!
res0: Int = 15
scala> val plist = Lz ~ yelp ~ (a+b)
plist: Lazy[Int] = Lazy#1ee1775
scala> b = 1
b: Int = 1
scala> lazyAdder(plist)
I'm adding now!
You evaluated me!
res1: Int = 9
Evidently repeated arguments are not available for by-name parameters.