ImplicitFunctionClass


#1

Dotty supports implicit function types
Would it be possible implement similar expansion for scala classes and traits:
for example:

@FunctionalInterface
abstract class ExtendedFunction extends Function0[Unit]{
  def apply():Unit
}
object Main extends App {
  def doSomeThing(f:ExtendedFunction):Unit = {}
  doSomeThing{
        println("ok")
  }
  println("Hello, World!")
}

The main idea of that proposal is that inside a function body of apply we have a context of the ExtendedFunction class. So we can use methods,variables and implicits of that class.

It will be very useful in dsl api.
See also:

There is FunctionalInterface in the java
The documentation says :
_Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references


Expunging `new` from scala 3
Expunging `new` from scala 3
PRE SIP: ThisFunction | scope injection (similar to kotlin receiver function)
Expunging `new` from scala 3
#2

The syntax you’re describing already exists

See https://www.scala-lang.org/news/2.12.0/#lambda-syntax-for-sam-types


#3

Unfortunately for classes it does not work

[E007] Type Mismatch Error: /tmp/scastie4527384693640219440/src/main/scala/main.scala:8:15 
8 |        println("ok")
  |        ^^^^^^^^^^^^^
  |        found:    Unit
  |        required: ExtendedFunction
  |        

#4

It works, but you have to write an actual lambda () => println("ok").
A semi-reasonable expectation could be that in

abstract class ExtendedFunction{
  def execute: Unit // notice: no parameter list
}
def doSomeThing(f: ExtendedFunction):Unit = {}

f would be treated like a by-name parameter, because execute sort of has type => Unit.

Until you can convince someone of actually speccing and implementing that, and having that custom ExtendedFunction class is important to you, you could consider this alternative:

abstract class ExtendedFunction{
  def execute: Unit
}
def doSomeThing(f: => Unit): Unit = {
  val f0 = new ExtendedFunction {
    def execute = f
  }
}

#5

Thanks, I have mistaken.

Really I do not need “actual lambda”, I need ImplicitFunction0

So that it would work:

 abstract class ExtendedFunction extends ImplicitFunction0[Unit]{
  def apply():Unit
}
object M{
  def doSomeThing(f: => ExtendedFunction):Unit = {}
  def main(args:Array[String]):Unit = {
      doSomeThing{
        println("ok")
    }
  }
}

#6

Syntax for SAM lambdas is the same, no matter if you’re implementing interface or extending trait or class.

Implicit functions are not about inferring empty parameter lists. They are about inferring implicit function parameters.


#7

Ok, I do not know how to make best proposal. Let’s do it together.
The main idea is creating ExtendedFunction without boilerplate.

I think if we have ImplicitFunction and lambda it will not be hard. And it will open new abilities in dsl libraries.


#8

It has no sense.

doSomeThing{ new ExtendedFunction{
        override def execute():Unit = {
            
        }
      }    

The main idea of that proposal is that inside a function body of execute we have a context of the ExtendedFunction class. So we can use methods,variables and implicits of that class.


#9

Ok, that wasn’t very apparent from your initial post. It’s also very different from how SAM types currently work.


#10

This works in Scala 2.12:

import scala.language.implicitConversions

object Scala {
  abstract class UncleSam {
    implicit def intToSet(int: Int): Set[Int] = Set(int)

    def definedMethod(x: Set[Int]): String = s"x = $x"

    def abstractMethod(self: UncleSam): Unit
  }

  def doSomeThing(lambda: UncleSam): Unit = {
    lambda.abstractMethod(lambda)
  }

  def main(args: Array[String]): Unit = {
    doSomeThing { self => // you need explicit parameter
      import self._ // and explicit import from it
      println(definedMethod(80))
    }
  }
}

There was already a proposal to define ThisFunction which would remove the need for

self =>
import self._

and you’ve posted a link to it in first post.

I would like something like the ThisFunction proposal for tests. Often I use something like:

"my class" must "do something important" in test { fixture =>
  import fixture._
  // rest of the test
}

Eliding whole fixture => + import fixture._ would be too disruptive for my taste, but something like:

"my class" must "do something important" in test { import fixture._ => // shortcut for previous syntax
  // rest of the test with fixture contents visible in scope
}

or

"my class" must "do something important" in test { this fixture =>
  // rest of the test with fixture contents visible in scope
}

would be more pleasing.


#11

We recently discussed eta expansion and expected types. Most recently at
https://github.com/scala/scala/pull/7660

The main reason we don’t do it for 0-ary SAM types is to avoid confusion when the interface is a SAM by accident (InputStream is a fun example.) We could do the expansion when the type is annotated with FunctionalInterface


#12

If I understand correctly eta expansion
It will be possible somthing like:

"my class" must "do something important" in test {
  val fixture  = this 
  // rest of the test with fixture contents visible in scope 
}

But I think it is more rare case. It is similar to self type.


#13

If I understand correctly, It have not been done yet.
Is there any real planning to do it?

@FunctionalInterface
trait ExtendedFunction extends Function0[Unit]{
  def apply():Unit
}
object Main extends App {
  def doSomeThing(f:ExtendedFunction):Unit = {}
  doSomeThing{
        println("ok")
  }
  println("Hello, World!")
}

Compiling 1 Scala source to C:\buf\scala\sbtTest\hello\target\scala-2.13.0-pre-116ca94-SNAPSHOT\classes ...
[error] C:\buf\scala\sbtTest\hello\src\main\scala\Main.scala:8:16: type mismatch;
[error]  found   : Unit
[error]  required: ExtendedFunction
[error]         println("ok")
[error]                ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 1 s, completed 26.02.2019 18:17:45

#14

Correct: it hasn’t been implemented, but I think it would make sense to do it.


PRE SIP: ThisFunction | scope injection (similar to kotlin receiver function)
#15

@adriaanm:
IIUC that still wouldn’t have any effect on this or visibility of ExtendedFuction members. this used in function passed to doSomething would still point to Main object. Members of ExtendedFunction wouldn’t be visible inside function passed to doSomeThing. Is that correct?


#16

Yes, I was only referring to the bit about eta expansion of 0-arity Sam types


#17

I do not think there will be a reasonable sense to do ExtendedFunction if members of ExtendedFunction are not visible inside function passed to doSomeThing. I really need better DalayedInit and Builders.
Are there any difficulties to do that visability?
Is it difficult?


#18

It’s just not what a lambda is. Its this is the class it’s contained in. Not sure about having a separate kind of function that changes the meaning of this (for one, I don’t think you could compile it to an invokedynamic lambda because these lambdas are not full classes on Java 8).

I can see the motivation for wanting to inject members into scope in a lambda, but not sure about the impact on language complexity.


#19

Sorry I just do not understand compiler internals well, Why shoud this proposal be a lambda?
Is it possible to make sugaring or something else to treat it as:

new ExtendedFunction{ override def execute():Unit = { 
   println("ok")
}

If it is hard at least I will not miss this feature :slight_smile:


#20

Let us just look (kotlin example)

class HTML() extend Tag {
    def head(init: Head):Head = initTag(init)
    def body(init: Body):Body = initTag(init)
}

class Head()extend Tag {
    def title(init: Title):Title = initTag(init)
}

class Title() extend Tag 

class BodyTag(name: String) extend Tag  {
    def b(init: B):Unit = initTag(init)
    def p(init: P):Unit = initTag(init)
    def h1(init: H1):Unit = initTag(init)
    def ul(init: UL):Unit = initTag(init)
    def a(href: String, init: A):Unit = initTag(init)
}

class Body extend BodyTag("body") 
class UL extend BodyTag("ul") {
    fun li(init: LI):Unit = initTag(init)
}

class B extend BodyTag("b")
class LI extend BodyTag("li")
class P extend BodyTag("p")
class H1 extend BodyTag("h1")

class A extend BodyTag("a") {
    var href: String
}

object Html{
  fun html(init: HTML): HTML = init.initTag
}

It is just impossible on “implicit function types”
Ok, we can make something similar:

class Table {
  val rows = new ArrayBuffer[Row]
  def add(r: Row): Unit = rows += r
  override def toString = rows.mkString("Table(", ", ", ")")
}

class Row {
  val cells = new ArrayBuffer[Cell]
  def add(c: Cell): Unit = cells += c
  override def toString = cells.mkString("Row(", ", ", ")")
}

case class Cell(elem: String)


object HtmlTable{
    def htmlTable(init: implicit Table => Unit) = {
      implicit val t = new Table
      init
      t
    }

    def row(init: implicit Row => Unit)(implicit t: Table) = {
      implicit val r = new Row
      init
      t.add(r)
    }

    def cell(str: String)(implicit r: Row) =
      r.add(new Cell(str))
}

I just do not know how it can be called a simple way.
There is context independent grammar so we have very high coupling, Methods is detached from its classes. There are very high risks of name clashes.
Ok we can always say it is not important. It is the question of priorities of cause. But I dream about such ability. It will make my life easier in jpa, html and so on :wink: