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

The link to

is this Extension Method

Thanks, yes, exactly that pull request (link fixed)

It is a pitty, but I think it will not help me much :frowning:

  • I would prefer to write html.table instead of %table because in such cases an operator has a magic sense. In our company it is bad style when you need look up headers to understand code. Such contraction can be used only in local code block.
  • The second greatest disadvantage of “implicit” in comparison to “receiver function” is absence of context dependent grammar . So I would afraid to use such approach in complex library like scalaFx I would prefer to use DelayedInit.
1 Like

Related links


By the way, I don’t know if this has been proposed here before, but you can use Dotty’s new keyword without having to specify the type when it can be inferred. This can make the kind of DSLs you want to design much more palatable:

head(new {
	someFunctionOnX() 
	other() 
})

You just needs these definitions:

inline def head(mk: => A): A {
  // do what you want before
  val res = mk // construct the object, which executes its constructor
  // do what you want after
  res
}
class A {
  def someFunctionOnX() = ...
  def other() = ...
}

Since head is inline, Dotty could probably easily remove the creation of the intermediate object.

Frankly, since the kind of syntax you want can be achieve with just the addition of a 3-character keyword and an additional pair of braces at the call sites, I think nothing would justify a language change like the one propose here.

I actually think having this new right there in the middle is a good idea, for code readability. Scala already has too much magic going on with its name resolution mechanism. Being able to see where injected scopes begin and end is a good thing IMHO.

2 Likes

Actually this example is not completely correct.
see

Kotlin reciever function do not use constructors for building. It is important thing.

I don’t see the problem. This use-case should be handled fine. Notice that I made the parameter to head by-name, which means that we can control the execution of the object creation.

Kotlin reciever function do not use constructors for building. It is important thing.

If you want to have access to an existing object, you could just make users new a proxy to that object:

class A { a =>
  def someFunctionOnX() = ...
  def other() = ...
  class Proxy {
    def someFunctionOnX() = a.someFunctionOnX()
    def other() = a.other()
  }
}
inline def head(a: A)(code: => a.Proxy): Unit = {
  code // execute the user's scope-injected code
}

val a = new A
head(a)(new{
  println(someFunctionOnX()) // scope injection!
})
1 Like

I’ve seen this after this PRESIP was made. Dotty has few features that’ll somehow tackle this problem. (new {...}) works but still is rather ugly. Implicit functions gives us similar freedom with less noise but implementation’ll be much more tricky.

I think you are very smart person(it is a complement, it is not joke).

I am sure you can make html dsl easily and you would not meet any problem.

But unfortunately there are more usual people.
I am sure if I would give him kotlin’s html dsl and your html dsl.
They would say that your version is whatever you want but it is not simplicity.

I need grammar as for smart people as for more usual.
I know that somebody think that ide is for people who are too lazy.
But I need the library for such people. I need that code assistant can support such library more better.
I need the library which can be supported by more usual people.

I do not know I want to save money or make the world a bit better :)))

IMHO I am sure that at least I will save money if such proposal is implemented :wink:
note.
It does not prove the need of this proposal.

You are right. If they solve the problem of name clashes. We can use implicit functions for example.

//decorate for init method call 
html(new{ //automate scope injection
   body{
       table{
       }
   }
})

But as I have said the code assistance will be awful.
The library will be very complicated, because of the global variables.
For example our dsl markup language has over 100 classes and over 500 properties between this classes.
When I think about it in a global list I feel some toothache :slight_smile:
We use pure xml now and I am not sure that dsl with implicits will have a sense.But we have java pojo classes and it is usefull.

If we make dsl for such markup one day I am not sure that usual people can support its code and documentation.

A use case will be type checking inside a context

for example

type Context

def (this: Context) infer(term: Term) = {
  term match {
    case Term.Pi(domain, codomain) =>
      extend(assertIsTypeAndEval(domain)).run {
         infer(codomain)
         // other stuff in the new context
      }
  }
}

Sorry to be that annoying person, but I’m unsure what the problem is that this addresses. Using out-of-the-box scala 2 features, I have fluent DSLs for HTML and JSON that ‘just work’ and have representational independence.

val myPage = 'html()(
  'head()(
    'title()("My page")),
  'body('class := "myStyle")(
    'h2('id := "ex_123")("This is my awesome page"),
    'div()("I wrote this page.")
  )
)

val js = jObject {
  'name := "Matthew",
  'food := "Fish" :: "Chips" :: Nil,
}

This uses an underlying tagless DSL for HTML and Json, respectively, and some syntactic flourishes to provide a := assignment syntax and to coerce native types into the DSL types. So you can interpret an HTML expression to, e.g., strings, or DOM, or harvest all IDs, or to validate classes. Similarly, the JSON dsl can be evaluated to JSON strings, to an ultra-light in-memory DOM, to first-cuts at type dictionaries from values, and so-forth. Now I will grant that there is some machinery involved in achieving this. Scala 3 makes this machinery much nicer. But I don’t see what a dedicated builder extension adds that is not met by using an appropriate tagless encoding with a suitable layer of sugar over the top. What does a builder extension add?

3 Likes

FYI symbol literals have been / are being dropped https://github.com/scala/scala-dev/issues/459

Yeah - I ran into that when I started porting this to scala 3 over a beer one night. The updated version allows string literals in place of the symbol literals.

IIUC:

val myPage = 'html()(
  'head(),'div()("I wrote this page.")
)

It will be compiled correctly.

It is not problem for json, it is not big problem for html(Html has a very flexible structure).
But for many dsl which have a lot of context dependent grammar.(sql,xml[with xsd],etc)
Static type checking can highly improve usability.

:))))
Sorry, I will not prove you that

  • static type cheking
  • auto code completion

is a very good
:))

If you are in the camp of dynamic schemes you just do not have such problems :wink:

That’s fair, but to @drdozer’s other point – is there anything you need that can’t be done with Scala 3’s implicit function types? I mean, I tend to think of this sort of strongly-typed context-specific syntax as the killer app for that feature…

The DSLs I showed are, intentionally, fairly unstructured. However, it is not difficult to extend these DLS to make them statically type checked, and provide auto completion. It would be relatively trivial to provide an alphabet of HTML tag names taken from e.g. the X-HTML DTD, and constrain that elements can only be nested within others as the DTD allows. The glue is all provided by an implicit that provides the .apply method to use the left-hand-side as if it were a constructor. I didn’t bother adding strong child type constraints because life is short, and these things are best done by code generation from the external type system, but it’s not in principle difficult to do. And you can still render it into a tagless abstraction which retains the ability to separate construction from interpretation.

The difference I think I’m discerning is that my style of building structures is a standard, immutable, referrentially transparent expression. Builder patterns seem to locally construct a mutable object and then set properties on it, before “releasing” it to the containing scope. That is, incidentally, the big difference between my HTML DSL and the one in scalatags.

I disagree with you, I think it is difficult in comparison to kotlin.
https://kotlinlang.org/docs/reference/type-safe-builders.html
And I have not seen any simple example which has comparable flexibility out of the box.

Yes, there is.

I just do not know how it can be simply done with implicit functions.
it is very simple with 5 functions, but when there are 500 -1000 functions it is implicit hell :slight_smile:

Kotlin receiver functions provide simple hierarchical scope management.

Perhaps I’m missing something. The builder pattern appears to work by using a block of statements to set mutable values in the object being built, together with some support for lifting into scope verbs for configuring that object. The tagless DSL approach works by configuring a value through an argument list (rather than block of statements) and the verbs need to be brought into scope by conforming to the argument type(s). I don’t want to get into a holy war about mutability vs expressions. But if the functionality we want to provide is a mechanism to flexibly build complicated, domain-specific expressions with type-safety, then it looks more like an IDE tooling issue to me than a language one. That the IDE can reliably suggest to you what verbs are available within the value building context.

1 Like