Implicit conversions for defs/lambdas in Scala? - scala

I just ran into a strange disparity between functions and objects (scala 2.10):
implicit def conv(c: Int => String) : (PrintStream => Int => Unit) = p => v => p.println(c(v))
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)
def a(x: Int) = x.toString
val b = (x: Int) => x.toString
// def main(args: Array[String]) = f(a) // fail
// def main(args: Array[String]) = f((x: Int) => x.toString) // fail
def main(args: Array[String]) = f(b) // ok
Why is there a difference between defs/lambda literals and lambda vals?
Update: apparently, the Problem does not occur for binary functions: Implicit conversion of a function to a second-order-function only works if the function to convert has at least two parameters
I checked this, and indeed the following code works:
implicit def conv(c: (Int,Unit) => String) : (PrintStream => Int => Unit) = p => v => p.println(c(v,()))
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)
def a(x: Int, y : Unit) = x.toString
val b = (x: Int, y : Unit) => x.toString
def main(args: Array[String]) = f(a) // ok
def main(args: Array[String]) = f((x: Int, y: Unit) => x.toString) // ok
def main(args: Array[String]) = f(b) // ok
Likewise, Nullary functions don't pose a problem, either:
implicit def conv(c: () => String) : (PrintStream => Int => Unit) = p => v => p.println(c())
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)
def a() = "1"
val b = () => "1"
def main(args: Array[String]) = f(a) // ok
def main(args: Array[String]) = f(() => "1") // ok
def main(args: Array[String]) = f(b) // ok
So, rephrasing the question: why does this not work for UNARY methods and functions?
Update: the problem also seems to be related to the target type (the type of f's argument h). The following also works (this time, in favour of "eta-expansion counts as hop", because we need to create a method value from a using _)
implicit def conv(c: Int => String) : Unit = ()
def f(h: Unit) : Unit = System.out.print("?")
def a(x: Int) = x.toString
val b = (x: Int) => x.toString
def main(args: Array[String]) = f(a _) // ok
def main(args: Array[String]) = f((x: Int) => x.toString) // ok
def main(args: Array[String]) = f(b) // ok

In scala defs are methods and are diffrent from functions.
scala> def a( x: Int, y: Int ): Int = x + y
a: (x: Int, y:Int)Int
scala> (x: Int, y: Int) => x + y
res0: (Int, Int) => Int = <function2>
You can convert a method to function by partially applying it.
scala> b _
res1: (Int, Int) => Int = <function2>
So.. you can do,
implicit def conv(c: Int => String) : (PrintStream => Int => Unit) = p => v => p.println(c(v))
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)
def a(x: Int) = x.toString
val af = a _
def main( args: Array[ String ] ) = f( af )
Alse, as #srgfed01 mentioned in his comment... for the second case the problem is that... they types are not explicitly specified, if you specify the type correctly... the second case will work.
scala> f( ( a => a.toString ): (Int => String) )
1
or
scala> f( ( _.toString ): (Int => String) )
1
Now, about differences between methods and functions...
You can call a method taking no arguments without parenthesis ()... but you can not call a function without ().
scala> def g() = 5
g: ()Int
scala> g
res15: Int = 5
scala> () => 5
res13: () => Int = <function0>
scala> res13
res14: () => Int = <function0>
scala> res13()
res15: 5
One of the most important reasons for methods being different from functions is because creators of Scala wanted seamless inter-interoperability with Java without being stuck with Java's limitations.
So methods (def) are very much similar to Java methods and keeping functions different from methods enabled them with limitless freedom to create Scala, the way they wanted.
Also... Another major difference is that methods can accept Type-classes where as functions can not. Basically you can have generic methods like
scala> :paste
trait Behave {
def behave
}
class A( elem: String ) extends Behave {
def behave() {
println( elem )
}
}
// Exiting paste mode, now interpreting.
defined trait Behave
defined class A
Now you can define a generic method,
scala> def check[ T <: Behave ]( t: T ): Unit = t.behave()
check: [T <: Behave](t: T)Unit
But you can not define a function like this,
scala> ( t: T ) => t.behave()
<console>:8: error: not found: type T
( t: T ) => t.behave()
or like this
scala> ( t: (T <: Behave) ) => t.behave()
<console>:1: error: ')' expected but '<:' found.
( t: (T <: A) ) => t.behave()

Related

Why scala cannot resolve the T parameter

When I try to call the following function:
def sortBy[T, R](seq: Seq[T], by: T => R)(implicit ordering: Ordering[R]): Seq[T] = {
...
}
with this:
case class MyClass(f1: Int, f2: Int)
val o1 = MyClass(1, 2)
val o2 = MyClass(3, 4)
sortBy(Seq(o1, o2), x => x.f1)
I get compilation error "cannot resolve symbol f1"
However when I call it with explicit types it works:
sortBy[MyClass,Int](...)
My question is why scala cannot infer these types automatically?
As one of the ways to solve
def sortBy[T, R](seq: Seq[T], by: T => R)(implicit ordering: Ordering[R]): Seq[T] = ???
case class MyClass(f1: Int, f2: Int)
val o1 = MyClass(1, 2)
val o2 = MyClass(3, 4)
def orderFunc(a: MyClass): Int = a.f1
sortBy(Seq(o1, o2), orderFunc)

Execute functions in a List

I have Object in scala in which I have defined some functions.
Object Sample {
val listFunction = Seq(func1(a,b),func2(p,q))
def func1(a: Int,b : Int) : Int ={
val c = a+b
c
}
def func2(p: Int,q : Int) : Int ={
val d = p+q
}
}
def main(args: Array[String]): Unit = {
//Want to call the list and execute the functions
ListFunction
}
How to call the list in main method and execute it?
Given
def func1(a: Int, b: Int): Int = a + b
def func2(p: Int, q: Int): Int = p + q
Consider the difference between
val x: Int = func1(2, 3) // applied function
val f: (Int, Int) => Int = func1 // function as value
So you have to use functions as values as you pass them to the sequence like so
val listFunction: Seq[(Int, Int) => Int] = Seq(func1, func2)
and then map over the list to apply the functions
listFunction.map(f => f.apply(2, 3))
listFunction.map(f => f(2, 3))
listFunction.map(_(2, 3))
scastie

Partial Functions as an input parameter in scala

I am trying to pass the partial function as input to square function.
What is worng with the following code?
package PartialFunction
object PartialFunctionSum {
val yourConstant = 10
val pf: PartialFunction[(Int, Int), Int] = {
case (x, y) => x + y + yourConstant
}
def square(cb:(Int, Int) => Int): Unit = {
println(cb(5,10))
}
def main(args: Array[String]): Unit= {
square(pf)
}
}
A PartialFunction is a Function1 (a => b) and square needs a Function2 ((a,b) => c)
One way to accomplish what you want to do is changing cb type to Function1.
object PartialFunctionSum extends App {
val yourConstant = 10
val pf: PartialFunction[(Int, Int), Int] = {
case (x, y) => x + y + yourConstant
}
def square(cb:((Int, Int)) => Int): Unit = {
println(cb(5,10))
}
square(pf)
}
Also, anywhere where you need a FunctionX, you can define a PartialFunction, so
square {
case (x, y) => x + y + yourConstant
}
Or transform your PartialFunction in a Function2 with a lambda
square( pf.apply(_, _) )
Anywhere you need a function you can a pass a PartialFunction,
Your problem is with square definition:
def square(cb:(Int, Int) => Int): Unit = {
println(cb(5,10))
}
That means that cb is a function that receives two Ints and returns another Int.
Just change square signature as follows:
def square(cb: ((Int, Int)) => Int): Unit = {
println(cb(5 -> 10))
}
And now your types are compatible and the rest of your code works as you expected.
(Int, Int) => Int is an instance of Function2[Int, Int, Int](a Function that takes 2 Int parameters and returns an Int), not PartialFunction[(Int, Int), Int] (a partial function that takes an input of type (Int, Int) and returns an Int)
If you want to pass your parameters as a Tuple then:
def square(cb:PartialFunction[(Int, Int),Int]): Unit = {
println(cb((5,10)))
}
Example:
def square(cb:PartialFunction[(Int, Int),Int]): Unit = {
println(cb((5,10)))
}
square: (cb: PartialFunction[(Int, Int),Int])Unit
scala> square{case (i, j) => i * j}
50

Implicit conversion is not applied if a method has type parameter

The question is based on the discussion here. This is the setup:
implicit def CToC2(obj: C1): C2 = {
new C2()
}
class C1() {
def f[U](f: (Int, Int) => U): U = f(1, 1)
}
class C2() {
def f[U](f: ((Int, Int)) => U): U = f(2, 2)
}
I'd expect that trying to call the function with a signature that exists in C2, scala would use the implicit conversion to satisfy the call:
val c1 = new C1()
val ff: ((Int, Int)) => Unit = t => println(t._1 + t._2)
But this fails:
scala> c1.f(ff)
Error:(16, 7) type mismatch;
found : ((Int, Int)) => Unit
required: (Int, Int) => ?
Interestingly if I drop the type parameter from C1, it works fine:
class C1() {
def f(f: (Int, Int) => Unit): Unit = f(1, 1)
}

How to make your own for-comprehension compliant scala monad?

I want to implement my own for-comprehension compatible monads and functors in Scala.
Let's take two stupid monads as an example. One monad is a state monad that contains an "Int" that you can map or flatmap over.
val maybe = IntMonad(5)
maybe flatMap( a => 3 * ( a map ( () => 2 * a ) ) )
// returns IntMonad(30)
Another monad takes does function composition like so...
val func = FunctionMonad( () => println("foo") )
val fooBar = func map ( () => println("bar") )
fooBar()
// foo
// bar
// returns Unit
The example may have some mistakes, but you get the idea.
I want to be able to use these two different types of made up Monads inside a for-comprehension in Scala. Like this:
val myMonad = IntMonad(5)
for {
a <- myMonad
b <- a*2
c <- IntMonad(b*2)
} yield c
// returns IntMonad(20)
I am not a Scala master, but you get the idea
For a type to be used within a for-comprehension, you really only need to define map and flatMap methods for it that return instances of the same type. Syntactically, the for-comprehension is transformed by the compiler into a series of flatMaps followed by a final map for the yield. As long as these methods are available with the appropriate signature, it will work.
I'm not really sure what you're after with your examples, but here is a trivial example that is equivalent to Option:
sealed trait MaybeInt {
def map(f: Int => Int): MaybeInt
def flatMap(f: Int => MaybeInt): MaybeInt
}
case class SomeInt(i: Int) extends MaybeInt {
def map(f: Int => Int): MaybeInt = SomeInt(f(i))
def flatMap(f: Int => MaybeInt): MaybeInt = f(i)
}
case object NoInt extends MaybeInt {
def map(f: Int => Int): MaybeInt = NoInt
def flatMap(f: Int => MaybeInt): MaybeInt = NoInt
}
I have a common trait with two sub-types (I could have as many as I wanted, though). The common trait MaybeInt enforces each sub-type to conform to the map/flatMap interface.
scala> val maybe = SomeInt(1)
maybe: SomeInt = SomeInt(1)
scala> val no = NoInt
no: NoInt.type = NoInt
for {
a <- maybe
b <- no
} yield a + b
res10: MaybeInt = NoInt
for {
a <- maybe
b <- maybe
} yield a + b
res12: MaybeInt = SomeInt(2)
Additionally, you can add foreach and filter. If you want to also handle this (no yield):
for(a <- maybe) println(a)
You would add foreach. And if you want to use if guards:
for(a <- maybe if a > 2) yield a
You would need filter or withFilter.
A full example:
sealed trait MaybeInt { self =>
def map(f: Int => Int): MaybeInt
def flatMap(f: Int => MaybeInt): MaybeInt
def filter(f: Int => Boolean): MaybeInt
def foreach[U](f: Int => U): Unit
def withFilter(p: Int => Boolean): WithFilter = new WithFilter(p)
// Based on Option#withFilter
class WithFilter(p: Int => Boolean) {
def map(f: Int => Int): MaybeInt = self filter p map f
def flatMap(f: Int => MaybeInt): MaybeInt = self filter p flatMap f
def foreach[U](f: Int => U): Unit = self filter p foreach f
def withFilter(q: Int => Boolean): WithFilter = new WithFilter(x => p(x) && q(x))
}
}
case class SomeInt(i: Int) extends MaybeInt {
def map(f: Int => Int): MaybeInt = SomeInt(f(i))
def flatMap(f: Int => MaybeInt): MaybeInt = f(i)
def filter(f: Int => Boolean): MaybeInt = if(f(i)) this else NoInt
def foreach[U](f: Int => U): Unit = f(i)
}
case object NoInt extends MaybeInt {
def map(f: Int => Int): MaybeInt = NoInt
def flatMap(f: Int => MaybeInt): MaybeInt = NoInt
def filter(f: Int => Boolean): MaybeInt = NoInt
def foreach[U](f: Int => U): Unit = ()
}