Refactoring val to method results in compile-time error - scala

I currently have
def list(node: NodeSeq): NodeSeq = {
val all = Thing.findAll.flatMap({
thing => bind("thing", chooseTemplate("thing", "entry", node),
"desc" -> Text(thing.desc.is),
"creator" -> thing.creatorName.getOrElse("UNKNOWN"),
"delete" -> SHtml.link("/test", () => delete(thing), Text("delete"))
)
})
all match {
case Nil => <span>No things</span>
case _ => <ol>{bind("thing", node, "entry" -> all)}</ol>
}
}
and I tried to refactor it to
def listItemHelper(node: NodeSeq): List[NodeSeq] = {
Thing.findAll.flatMap({
thing => bind("thing", chooseTemplate("thing", "entry", node),
"desc" -> Text(thing.desc.is),
"creator" -> thing.creatorName.getOrElse("UNKNOWN"),
"delete" -> SHtml.link("/test", () => delete(thing), Text("delete"))
)
})
}
def list(node: NodeSeq): NodeSeq = {
val all = listItemHelper(node)
all match {
case Nil => <span>No things</span>
case all: List[NodeSeq] => <ol>{bind("thing", node, "entry" -> all)}</ol>
case _ => <span>wtf</span>
}
}
but I get the following. I've traced all the return types and I don't see how my refactoring is any different than what would be happening internally. I even tried adding more match cases (as you can see in the refactored code) to make sure I was selecting the right type.
/Users/trenton/projects/sc2/supperclub/src/main/scala/com/runbam/snippet/Whyme.scala:37: error: overloaded method value -> with alternatives [T <: net.liftweb.util.Bindable](T with net.liftweb.util.Bindable)net.liftweb.util.Helpers.TheBindableBindParam[T] <and> (Boolean)net.liftweb.util.Helpers.BooleanBindParam <and> (Long)net.liftweb.util.Helpers.LongBindParam <and> (Int)net.liftweb.util.Helpers.IntBindParam <and> (Symbol)net.liftweb.util.Helpers.SymbolBindParam <and> (Option[scala.xml.NodeSeq])net.liftweb.util.Helpers.OptionBindParam <and> (net.liftweb.util.Box[scala.xml.NodeSeq])net.liftweb.util.Helpers.BoxBindParam <and> ((scala.xml.NodeSeq) => scala.xml.NodeSeq)net.liftweb.util.Helpers.FuncBindParam <and> (Seq[scala.xml.Node])net.liftweb.util.Helpers.TheBindParam <and> (scala.xml.Node)net.liftweb.util.Helpers.TheBindParam <and> (scala.xml.Text)net.liftweb.util.Helpers.TheBindParam <and> (scala.xml.NodeSeq)net.liftweb.util.Helpers.TheBindParam <and> (String)net.liftweb.util.Helpers.TheStrBindParam cannot be applied to (List[scala.xml.NodeSeq])
case all: List[NodeSeq] => <ol>{bind("thing", node, "entry" -> all)}</ol>
^

Here's how my brain parsed the error message...
error: overloaded method value ->
This is the name of the method, which is '->'.
with alternatives
What will follow is the list of possible parameters for -> within the bind() function.
[T <: net.liftweb.util.Bindable](T with net.liftweb.util.Bindable)net.liftweb.util.Helpers.TheBindableBindParam[T]
This says that anything which implements or includes the trait Bindable is fair game.
<and> (Boolean)net.liftweb.util.Helpers.BooleanBindParam
<and> (Long)net.liftweb.util.Helpers.LongBindParam
<and> (Int)net.liftweb.util.Helpers.IntBindParam
<and> (Symbol)net.liftweb.util.Helpers.SymbolBindParam
<and> (Option[scala.xml.NodeSeq])net.liftweb.util.Helpers.OptionBindParam
<and> (net.liftweb.util.Box[scala.xml.NodeSeq])net.liftweb.util.Helpers.BoxBindParam
Bunch of type-specific options.
<and> ((scala.xml.NodeSeq) => scala.xml.NodeSeq)net.liftweb.util.Helpers.FuncBindParam
<and> (Seq[scala.xml.Node])net.liftweb.util.Helpers.TheBindParam
<and> (scala.xml.Node)net.liftweb.util.Helpers.TheBindParam
<and> (scala.xml.Text)net.liftweb.util.Helpers.TheBindParam
<and> (scala.xml.NodeSeq)net.liftweb.util.Helpers.TheBindParam
<and> (String)net.liftweb.util.Helpers.TheStrBindParam
Ah! Node-related stuff. Our valid options seem to be NodeSeq, Seq[Node], Text, and Node
cannot be applied to (List[scala.xml.NodeSeq])
Looks like List[NodeSeq] is not a valid option.
With this in mind, you probably want to take an individual NodeSeq out of the List in order to bind it to the form. Are you sure you really want to return a List from the helper method?

I failed to see that NodeSeq extends Seq[Node], so I had the wrong return type on the extracted method. Changing it to
def listItemHelper(node: NodeSeq): NodeSeq = {
Thing.findAll.flatMap({
thing => bind("thing", chooseTemplate("thing", "entry", node),
"desc" -> Text(thing.desc.is),
"creator" -> thing.creatorName.getOrElse("UNKNOWN"),
"delete" -> SHtml.link("/test", () => delete(thing), Text("delete"))
)
})
}
def list(node: NodeSeq): NodeSeq = {
val all = listItemHelper(node)
all.length match {
case 0 => <span>No things</span>
case _ => <ol>{bind("thing", node, "entry" -> all)}</ol>
}
}
works.

One issue is that your match doesn't really make any sense: basically you are matching against either an empty list or a non-empty list. There is no other possibility:
all match {
case Nil => //if list is empty
case nonEmptyList => //if list is not empty
}
Of course you could also do:
case Nil =>
case x :: Nil => //list with only a head
case x :: xs => //head :: tail

As a side note, there's one thing in your code that doesn't work:
case all: List[NodeSeq]
Because of type erasure, there's no way to test, at runtime, whether all list a List[NodeSeq], List[String], List[AnyRef] or what-have-you. I'm pretty sure you must be getting a warning on that line, and ignoring it because you don't understand what it is warning you about (at least, that's what happened to me when I got such warning :). The correct line would be:
case all: List[_]
Which would accept any kind of List. Look up a question of mine on type erasure and Scala to see a bit more about it, if you are interested.

Related

Implementing functions returning match-based dependent types in scala 3

I'm experimenting with describing computations using functional composition in Scala 3. The goal is to be able to write something like this:
def doThat(v: Val[String]): Val[String] =
v.maybeMap((s) => if s.length < 3 then () else s.capitalize) // fn1
.map(_.substring(0, 3)) // fn2
The idea is that Val has semantics similar to Option, so fn2 would never be called for arguments shorter than 3 characters because fn1 would return Unit. Note that I don't just use Option instead of Val here because I'm interested in capturing the structure of the computation, not the result of a computation with a given argument, so Val is a placeholder for a value, not an actual value.
The goal is to be able to invoke doThat like this:
enum ValType:
case Just, Option
class ValImpl[I](val t: ValType, val fn: ((Any) => Any) | Unit, val input: Val[Any] | Unit) extends Val[I]:
// snipped
v = doThat(ValImpl(ValType.Just, (), ()))
and get a result that looks like ValImpl(Just, fn2, ValImpl(Option, fn1, ValImpl(Just, (), ())))
I defined Val the following way:
trait Val[I]:
def map[R](fn: (Val.Unwrap[I]) => R): Val.SomeOf[R]
def maybeMap[R](fn: (Val.Unwrap[I]) => R | Unit): Val.OptionOf[R]
object Val:
type SomeOf[I] = Val[I]
type OptionOf[I] = I match
case Option[i] => I
case _ => Val[Option[I]]
type Unwrap[I] = I match
case Option[i] => i
case _ => I
The rationale for using dependent types for parameters of map and maybeMap is that if they are invoked on Val[Option[I]], the fn would still be written with I as an argument. Further, maybeMap returns dependent type to avoid returning Val[Option[Option[I]] if invoked on Val[Option[I]].
To this point it all compiles. However, now I am trying to implement ValImpl like this:
case class ValImpl[I](val t: ValType, val fn: ((Any) => Any) | Unit, val input: Val[Any] | Unit) extends Val[I]:
// helps to make the code more readable
private def eraseType[P, R](fn: (P) => R) = fn.asInstanceOf[(Any) => Any]
private def eraseType[P](v: Val[P]) = v.asInstanceOf[Val[Any]]
def map[R](fn: (Val.Unwrap[I]) => R) = this match
case _ : ValImpl[Option[I]] => ValImpl(ValType.Option, eraseType(fn), eraseType(this))
case _ : ValImpl[I] => ValImpl(ValType.Just, eraseType(fn), eraseType(this))
def maybeMap[R](fn: Unwrap[I] => R | Unit) = this match
case _ : ValImpl[Option[I]] => ValImpl[Option[R]](ValType.Option, eraseType(fn), eraseType(this))
case _ : ValImpl[I] => ValImpl[Option[R]](ValType.Option, eraseType(fn), eraseType(this))
I am getting the following compilation errors in method ValImpl.maybeMap:
Test.scala:39:54
Found: test.ValImpl[Option[R]]
Required: test.Val.OptionOf[R]
case _ : ValImpl[Option[I]] => ValImpl[Option[R]](ValType.Option, eraseType(fn), eraseType(this))
Test.scala:40:46
Found: test.ValImpl[Option[R]]
Required: test.Val.OptionOf[R]
case _ : ValImpl[I] => ValImpl[Option[R]](ValType.Option, eraseType(fn), eraseType(this))
Please can someone explain why the compiler struggles with these lines? I cannot understand why ValImpl[Option[R]] in this case is not also Val.OptionOf[R].
Any suggestions on how to fix this problem in the code are also appreciated.
I found a way past this once I realised that type calculations and the actual runtime behaviour exist independently. Even though the compiler tries to enforce alignment, it is not able to do so all the time, and at times you need to nudge it with .asInstanceOf[]. Perhaps it's completely misguided, but no matter what I tried I was not able to get it to realise that Val.OptionOf[I] is the same as thing as Val[Option[I]].
I also realised that matching by the type parameter in the implementations of map and maybeMap is pointless as that information would not be available at runtime.
So, the working code looks like the following:
case class ValImpl[I](val t: ValType, val fn: ((Any) => Any) | Unit, val input: Val[Any] | Unit) extends Val[I]:
// helps to make the code more readable
private def eraseType[P, R](fn: (P) => R) = fn.asInstanceOf[(Any) => Any]
private def eraseType[P](v: Val[P]) = v.asInstanceOf[Val[Any]]
def map[R](fn: (Val.Unwrap[I]) => R) =
ValImpl(ValType.Option, eraseType(fn), eraseType(this)).asInstanceOf[Val.OptionOf[R]]
def maybeMap[R](fn: Unwrap[I] => R | Unit) =
ValImpl[Option[R]](ValType.Option, eraseType(fn), eraseType(this)).asInstanceOf[Val.OptionOf[R]]

Error while passing a tuple to a generic method

Here is a function to memoize /cache intermediate result :
def memoize[I, O](f: I => O) = new scala.collection.mutable.HashMap[I, O]() {
override def apply(key: I): O = getOrElseUpdate(key, f(key))
}
This works fine for code like below,
val double: Int=>Int = memoize {
_*2
}
However, when I try to use tuple as input parameter(I) it shows compile time error,
val isGivenNumIsHead:(List[Int], Int) => Boolean = memoize {
case (Nil, _) => false
case (a:: as, n) => a == n
}
Compile time error is :
Expression of type mutable.HashMap[Nothing, Boolean] {def apply(key: Nothing): Boolean} doesn't conform to expected type (List[Int], Int) => Boolean
Is this something related to erasure.
How do i fix it ?
I am assuming you want to use the tuple as the key in the HashMap. With that in mind, here is the explanation.
The actual return type of memoize is scala.collection.mutable.HashMap[_,_] . That is being assigned to double which is of type Int => Int or Function1[Int,Int] ( a function that takes an integer and gives an interger). The compiler doesnt throw an error because mutable.HashMap extends scala.collection.mutable.MapLike which in turn extends scala.collection.MapLike which in turn extends scala.PartialFunction[A, B] which in turn extends scala.Function1[A, B]. Hence there is no compilation error.
On the other hand, the syntax for functions taking one parameter and returning one value is val functionName : A => B = a => {return b} or can be written as val function : (A) => B = a => {return b} or val function: (A => B) = a => {return b}. You have used the second method. In that case, the value of A should be of single type. You have used List[Int],Int which is not a single type. Note that I intentionally removed the brackets. So in order to make that as a single type and to pass it as a tuple, you have to use one more set of brackets. The correct syntax would be
val isGivenNumIsHead:((List[Int], Int)) => Boolean = memoize {
case (Nil, _) => false
case (a:: as, n) => a == n
}
Note the usage of additional brackets to make it a tuple.

Scala / Play 2.5: Overloaded method apply with alternatives

I am using Scala & Play 2.5. I am stuck with this error:
Game.scala:99: overloaded method value apply with alternatives:
[error] (block: => play.api.mvc.Result)play.api.mvc.Action[play.api.mvc.AnyContent] <and>
[error] (block: play.api.mvc.Request[play.api.mvc.AnyContent] => play.api.mvc.Result)play.api.mvc.Action[play.api.mvc.AnyContent] <and>
[error] [A](bodyParser: play.api.mvc.BodyParser[A])(block: play.api.mvc.Request[A] => play.api.mvc.Result)play.api.mvc.Action[A]
[error] cannot be applied to (Object)
[error] def start(id: String, apiKey: Option[String]) = Action {
This is the function:
def start(id: String, apiKey: Option[String]) = Action {
apiKey match {
case Some(API_KEY) => {
Server.actor ! Server.Start(id)
Ok("Started")
}
case _ => Future.successful(Unauthorized)
}
}
The problem is, the result of the match statement has been inferred to be Object, since from one case statement you're returning Result, and from the other you're returning Future[Result], so the only common super type is Object. To fix, change case _ => Future.successful(Unauthorized) to case _ => Unauthorized.

How do I define a function parameter default associated with a generic parameter?

I am attempting to refactor a function (found towards the end of this StackOverflow answer) to make it slightly more generic. Here's the original function definition:
def tryProcessSource(
file: File,
parseLine: (Int, String) => Option[List[String]] =
(index, unparsedLine) => Some(List(unparsedLine)),
filterLine: (Int, List[String]) => Option[Boolean] =
(index, parsedValues) => Some(true),
retainValues: (Int, List[String]) => Option[List[String]] =
(index, parsedValues) => Some(parsedValues)
): Try[List[List[String]]] = {
???
}
And here is to what I am attempting to change it:
def tryProcessSource[A <: Any](
file: File,
parseLine: (Int, String) => Option[List[String]] =
(index, unparsedLine) => Some(List(unparsedLine)),
filterLine: (Int, List[String]) => Option[Boolean] =
(index, parsedValues) => Some(true),
transformLine: (Int, List[String]) => Option[A] =
(index, parsedValues) => Some(parsedValues)
): Try[List[A]] = {
???
}
I keep getting a highlight error in the IntelliJ editor on Some(parsedValues) which says, "Expression of type Some[List[String]] doesn't conform to expected type Option[A]". I've been unable to figure out how to properly modify the function definition to satisfy the required condition; i.e. so the error goes away.
If I change transformLine to this (replace the generic parameter A with Any)...
transformLine: (Int, List[String]) => Option[Any] =
(index, parsedValues) => Some(parsedValues)
...the error goes away. But, I also lose the strong typing associated with the generic parameter.
Any assistance on with this is very much appreciated.
In transformLine: (Int, List[String]) => Option[A] = (index, parsedValues) => whatever, parsedValues obviously has type List[String], and so Some(parsedValues) is Some[List[String]]. There is simply no reasonable default value for transformLine.
You can change its type to (Int, A) => Option[A] or (Int, List[A]) => Option[List[A]] depending on what you need, and change the previous arguments as well, but you'll get stuck on the fact that lines are Strings, so you'll have to convert them to A somewhere.
After reading and re-reading Alexey Romonov's answer and Lee's comment, it gave me a clue into how to refactor this to get it working. Additionally, I think it might also implies a guideline regarding providing default values to a function.
If I am understand it correctly, I am trying to combine a concrete, in this example case List[String] in the transform parameter default, with a generic type, in this case Option[A]. Essentially, this overlaps the boundary upon which a generic copy of the function is instantiated by the compiler for the specific user provided type and the explicitly provided List[String]. This leads me to the following guideline regarding defining generic functions:
When transforming a concrete function to make it generic,
each parameter with a default value which interacts directly with any of the generic parameters likely needs to
be removed and placed into an enclosing concrete function which forwards the call to the generic function.
So with this guideline in mind, let's rework the original code...
The concrete function looks like this (very similar to the original pre-generic-ified version which also included the desired concrete default parameters):
def tryProcessSource(
file: File,
parseLine: (Int, String) => Option[List[String]] =
(index, unparsedLine) => Some(List(unparsedLine)),
filter: (Int, List[String]) => Option[Boolean] =
(index, parsedValues) => Some(true),
transform: (Int, List[String]) => Option[List[String]] =
(index, parsedValues) => Some(parsedValues)
): Try[List[List[String]]] =
tryProcessSourceGeneric(file, parseLine, filter, transform)
And the generic-ified function looks like this (notice the lack of any parameter default values):
def tryProcessSourceGeneric[A, B](
file: File,
parseLine: (Int, String) => Option[A],
filter: (Int, A) => Option[Boolean]
transform: (Int, A) => Option[B]
): Try[List[B]] = {
???
}
Again, a huge thank you to Alexey and Lee for helping me struggle through this.

Type bounds on functions

I am having trouble getting my code with parameterized types to pass the scala compiler. My goal is to be able to express (Predicate, Action) pairs as shown in the MyProg object.
trait ProgBase {
type Predicate[T] = T => Boolean
type Action[T] = T => Unit
private var prog = List[(Predicate[Any], Action[Any])]()
final def on[T <: Any](pred: Predicate[T])(action: Action[T]) = {
prog = (pred, action) :: prog // gives type mismatch
}
// remainder of trait elided
}
object MyProg extends ProgBase {
on[String](s => !s.isEmpty) { s =>
println(s + " is not empty")
}
on[Int](i => i.isValidByte) { i =>
println(i + " can fit in a byte")
}
}
By specifying that T has an upper bound of Any, I hoped this would appease the compiler, but clearly I am missing something:
[error] ......ProgBase.scala:8 type mismatch;
[error] found : (T => Boolean, T => Unit)
[error] required: (Any => Boolean, Any => Unit)
[error] prog = (pred, action) :: prog
[error] ^
First of all, answer to your question, if you write:
private var prog = List[(Predicate[_ <: Any], Action[_ <: Any])]()
It all compiles OK. We should use wildcards, 'cause the type of elements are unknown.
Second, maybe you mistyped, you can't use your on function as you used it, use it something like:
on[String](s => !s.isEmpty)(s => !s.isEmpty)
It caused by type mismatch: type Action[T] = T => Unit but println has type Unit,
so as a variant u can simply write: type Action = Unit. Obviously, u can avoid using this type alias at all.
Third, maybe you already know, and I shoudn't tell you, that in fact, you lose all information about predicate types - let's check it using Scala reflection:
import scala.reflect.runtime.{universe => ru}
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
val s: String = "123"
val i: Int = 123
on[String](s => !s.isEmpty)(s => !s.isEmpty)
on[Int](i => i.isValidByte)(i => i.isValidByte)
getTypeTag((MyProg.prog.head._1)).tpe =:= ru.typeOf[(String) => Boolean] //>false!
So you see the problem.
To deal with it you can use heterogeneous lists. Lists and other various powerful structures you can find in shapeless: https://github.com/milessabin/shapeless