Updated Proposal: Revisiting Implicits

I feel uneasy about the ?=> syntax, in this case I might prefer more regularity. The only annoying thing is that

def f(x: (using A) => B)

is more noisy than I would prefer, but that on itself doesn’t feel to be enough of a justification.

When the => syntax for givens was introduced you counterargued to this same argument saying that this is a very uncommon use case (if not outright an antipattern), and that simple things should be easy and complex things possible. Something along these lines. Now I don’t necessary like how close these 2 are either, but I think your argument was valid back then and it is still valid now.

1 Like

The aspect that guided almost all recent syntax decisions was the empirical experience. Between compiler, library, tests and community build we usually have many hundreds of occurrences of each construct. That’s a great way to test and adjust our assumptions. In this case, the adjustment was: context function types are not that rare (and should become more common once they are better known) and that writing using (or given at the time) makes them quite hard to read. So I had to revise my earlier assumptions.

The whole process has actually been really interesting. I believe it is quite rare to be able to do this kind of empirical language design.

3 Likes

Sounds like a great source of input and I’m glad that it contributes to the quality of Scala 3!

If the experience with ?=> is positive then great, sounds like that extra noise of givens/usings is more annoying than I thought.

2 things on my mind:

1

Not sure what effect it has on my question wrt. power equivalence of methods and functions. Or if such equivalence is a goal to begin with. Or if it could be made into a goal, if not already. I would find it both useful and logically consistent, or maybe aesthetic.

2

This to me sounds very supportive of…

…of my argument to not base decisions wrt. given syntax on anticipated (subjective) obscurity since the same syntax in other areas (e.g. context functions as confirmed by you, polymorphic functions) might become much more popular than anticipated rendering it less/not (subjectively) obscure.

edit: even if I might have overstated the lack of production code, that argument seems to me to hold and being supported by what you shared

IMHO the current result is much more better.

Could you explain. why is it worse to have:

lower camel case for names of given instances at grammar level?

It is just reduce amount of different styles.

the ability to define a need to create fresh instance on each reference without dummy names

      given laterInit[_] as Context = ....
      given laterInit[] as Context = .... 
      given laterInit() as Context = .... 

I can imagine that there are no differences for the compilator at all. But I am sure dummy names will confuse people at first

Huh. A ?=> B is the symbolic form of PartialFunction[A, B] in my world. :laughing:

2 Likes

Well, it’s a reserved word now, so we have to find something else for partial functions. Maybe

partial_=>

?

Huh. I’ve always been partial to =?>. (please don’t reserve that, too!)

2 Likes

he he: It seams that =?> is used in scalalib in:
scala.sys.process.processInternal.=?>[T,E] == PartialFunction[T,E]

1 Like

Just to explore all the options or be the Tenth Man

val x: Int = given 4
def t3[T, U, V](using st: Show[T], su: Show[U], sv: Show[V]): Show[(T, U, V)] = given Show(...) { }

and another alternative

val x: given Int = 4
def t3[T, U, V](using st: Show[T], su: Show[U], sv: Show[V]): given Show[(T, U, V)] = Show(...) { }

second alternative also has a nice has a nice construction/deconstruction symmetry

val (ctx: given Context, n: given Int) = (ForkJoinContext(), 42)

The syntax for both of these probably would probably require no explanation to a scala beginner

To be honest, I find the difference between

def f(x: (using A) => B)

and

def f(using x: A => B)

clearer than

def f(x: A ?=> B)

Though that might be because I am not yet very familiar with the new syntax. In any case, the regularity seems to help readability (IMO).

8 Likes

Yeah, I picked it up from Paul, and he switched from =?> to ?=> because ?=> better mirrors isDefinedAt then apply. I guess I’ll just switch back.

1 Like

I’m skeptical that the A ?=> B syntax is a good idea, for two reasons, which I think have kind of been stated, but I want to state them directly:

  1. (using A) => B perfectly mirrors the method declaration, while A ?=> B adds an extra nonstandard symbol to learn.
  2. ?=> intuitively makes more sense as PartialFunction (meaning “maybe I can A => B”) than it does as a context function type (“I’ll produce B, just let me know A from context”).

If we wanted to do it symbolically, =?> or ?=> would be for PartialFunction (and the other wouldn’t be used, to avoid confusion), and some other weird arrow would be used for context functions, like ~~>. (Tilde suggests a wiggly indirect path, which is kinda true if it’s context-dependent.)

But I’m not sure that we ought to do it symbolically. (using A) => B is really clear.

Of course we then have the problem of def foo(using A => B) being confusing (because it looks like (using A) => B, even though it’s not valid to just stick a type as an argument with nothing else). If we want this, we can warn on using A => B and require using (A => B).

Anyway, although I normally adore symbolic methods, I think ?=> is not a great choice here.

Edit: some IMO better choices include

  1. ~> “indirect arrow”: upside – looks like a not-utterly-straight path to get the result; downside – already used in Akka (edges), Graph for Scala (directed edges), Cats (mapping), etc.
  2. ~~> “long indirect arrow” upside – same as ~> and it doesn’t conflict with much; downside – long! Why so long?!
  3. *=> “take all stuff and map”: upside – * is used as a kind of gathering option already; downside – * only falls in the middle in some fonts, making it look arrowlike
  4. !=> “wow! then map”: upside – ! indicates something extraordinary is going on (in this case, using givens); downside – doesn’t look that much like an arrow because ! is both wide and asymmetric; also suggests “not” as well as “wow”
15 Likes

I was thinking a plus sign might make some sense (it’s a function plus some hidden arguments), so =+> or +=>. But better no special arrow. We could always add one later as a shortcut if there is enough demand.

2 Likes

I see a few problems with A ?=> B:

  • val f: A ?=> B is the same as def f(using A): B. But there is no self-explanatory link between using and ?=> here.
  • For anyone new to the language, who might have to look up the symbol, it’s a mess because searching for symbolic operators on the internet is close to impossible due to such symbols not being indexed by most search engines. Because of this, symbolic operators should be avoided as long as it’s not a very self-explanatory operator that is obvious also to newcomers.

As suggested by others, val f: (using A) => B is more self-explantory, and I believe it can more easily be understood to mean the same as def f(using A): B.

7 Likes

I feel the same kind of discomfort here

I’m just adding to the general crowd a voice here, I can imagine, thinking of a notation that could somehow suggest the meaning.

My proposal is a choice between

def f(x: A |- B) // proposal 1

def f(x: A |-> B) // proposal 2

def f(x: A |=> B) // proposal 3

with the idea of recalling the typing environment in type theoretic notation: we have a context that provides values of a given type…

MIght not be too wide-spread a notion.
I can’t say if any of those operators could conflict with existing codebases or libraries.

I had considered all of these before, and propsed |=> in an earlier iteration. I see where you are coming from. But in the end, I believe ?=> is easiest to grok for non-type-theorists. Also, it happens to be essentially the same notation as in the POPL paper.

Thanks for the answer, I admit it’s quite hard to keep track of all that happened since the original proposal

Regular text cannot be a part of an operator symbol because spaces do not matter

The topic of implicit conversions has come up repeatedly in this thread.

I’ve now started a new thread devoted only to conversions: Proposal: Changes to Implicit Conversions . It includes summaries and links to what has already been said here.

Although all aspects of implicits are related, the subject of conversions is at least somewhat independent, so I hope we can centralize further talk about conversions on the new thread.

3 Likes