Re-reading the “Given without as” PR, I think I understand better why abstract given is needed:
Now, one intriguing step further is whether we want to allow the same convention for normal defs. I.e
def foo(x: Int): T with {...}
as a slightly more powerful alternative to
def foo(x: Int) = T with {...}
or, using new
def foo(x: Int) = new T {...}
with the same expansion as for the given. This would give us parameterized objects, or functors in the SML sense,
without having to define a class. It also gives a nice way to avoid duplication between return type and RHS while stile having an explicitly defined return type
In that view, removing abstract given would also mean annihilate normal abstract definitions.
What we have here is a type ascription that can produce a value. It is shockingly irregular. While interesting, it is research material. Fortunately, it is not considered for 3.0, and maybe we could take a step back and disable abstract given too.
given c: C // abstract
given c: C = new C // alias
given C = new C // anonymous
given C // instance; no confusion with abstract since abstract cannot be anonymous
given c extends C // named instance
I still think it would be good to have this in RC1. It’s just a syntax change - substitute extends for : in the given instance case, and eliminate with. So we might as well target 3.1 if there are more urgent issues.
My main concern is that currently the design of given is at odds with braceless syntax and gets in our way in that respect:
It mandates braces if we want to use parameter vs member abstraction:
given WithParameters[Int](1) with {}
It breaks the rule that (keyword, operator) starts an indented block and : starts a template body
Edit: what about new instead of extends ? Instance could be seen as an abridged version of alias:
given c: C = new C
given c new C
This time we could use it in anonymous, solving a lot of ambiguities:
given new C
given [T : Ord] new Foo[T]
Even better, we could use both - new for anonymous and extends for named instance:
That’s how I see it. Given syntax seems to be largely about making names optional, and Scala 2 already has a mechanism for omitting names. The most straightforward thing would be to just use it.
I think that with given we want to insist on the type, rather than the value, hence we want to make the latter as invisible as possible. And you would want to do that also for using.
using _: C
And yes, there’s the creator application question too.
Thinking about it, what is “researchy” about the current design is related to member abstraction. As I understand it, that’s how it’s encoded in the underlying calculus (DOT).
given WithMembers with {
type A = Int
val foo = 1
}
Removing braces does not help that much.
given WithMembers with
type A = Int
val foo = 1
On the contrary, parameter abstraction is naturally braceless.
What about for ? It works in both named and anonymous.
given c for C
given for C
Edit: overall picture:
given c: C // abstract
given c: C = new C // alias
given C = new C // anonymous
given for C // instance
given c for C // named instance
Edit2: what about bringing back as just in the instance case?
given c: C // abstract
given c: C = new C // alias
given C = new C // anonymous
given as C // instance
given c as C // named instance
Advantages:
We wouldn’t have to rewrite all material produced so far
It is compatible with the main argument in the “given without as” discussion that we don’t want given value as type, as in the instance case it is not type but constructor
Edit3: why not with ?
given c: C // abstract
given c: C = new C // alias
given C = new C // anonymous
given with C // instance
given c with C // named instance
Here is my latest attempt to get rid of with in given, in order to smooth the discussion in the “other thread”. I think everybody will agree that extends makes most sense here:
given c extends C
The problem is with anonymous:
given extends C
Revolting might be the word (less so if we take given as a substantive but let’s ignore this for the moment). What if we take instance (is that a soft keyword ?) to fill the blank in the anonymous case:
given instance extends C
given instance [T : Ord] extends C[T]
A little strangeness to solve the bigger with strangeness…
Edit : or indeed underscore, just in the instance case:
given c: C // abstract
given c: C = new C // alias
given C = new C // anonymous
given c extends C // instance
given _ extends C // instance, anonymous
Edit2 : or maybe take instance also in the named case:
given c: C // abstract
given c: C = new C // alias
given C = new C // anonymous
given instance extends C
given instance c extends C // named
I was initially against instance because the term is too general (not just for type classes), and there is potential for confusion with isInstanceOf and asInstanceOf. But I admit your examples look nice.
instance is a pretty common variable name, especially in java libraries and “scala as a better java” code bases. I think it expresses the meaning very well, but the overlap with old code could cause nuanced dissonance.
extends, with , and : , (plus _ for anonymity) read very comfortably for me - all three are synonyms in my head. However, I think I’m late to this party.
There were no instance declarations, yet the test returns true. Indeed, the name of isInstanceOf would now send the wrong signal about its meaning. As another example, some people will probably end up wanting to write a test that a type Foo has a type class instance for C as Foo.isInstanceOf[C].
given c: C // abstract
given c: C = new C // alias
given C = new C // anonymous
given instance of C
given instance c of C // named
given instance [T : Ord] of C[T] // with params
Edit : better syntax yet:
given instance C // remove `of` in anonymous
given instance c extends C // replace `of` by `extends`
given instance [T : Ord] extends C[T] // with params
Edit2 : maybe even better, remove instance in named case, to better match abstract in case of implement:
given c: C // abstract
given c: C = new C // alias
given C = new C // anonymous
given instance C // instance
given c extends C // instance, named ; abstract implement
given instance [T : Ord] extends C[T] // with params
The syntax is different if C is a type or a constructor. I think it might be better on a educational point of view.
What I meant is, it should be obvious to anyone that it is about a typeclassinstance.
Edit : I would not see the point of looking at runtime if something is in the implicit scope, as it is a compile-time notion.
So, the use of instance as a (soft) keyword in given is absolutely not ambiguous, in my opinion. I am sorry to have to constantly revive this thread, but I think the issue is still not addressed. What is the problem with my instance-based solution above, compared to given with ? If none, what prevents us to implement it ? Thanks.
Personally, I think there’s more room for confusion and no actual gain. I dislike “summon” but it’s very peculiarity will make it easy for people to remember it after learning about it. Conversely, instanceOf looks primed to get newcomers to the language confused, particularly Java programmers. The fact that a familiar term is used can make them believe they know what it is doing, whereas the unusual summon will prompt them to look it up.
Sorry, as I was not actually proposing to substitute instanceOf for summon. This was in response to @LPTK 's concern that instance as a keyword is misleading. In my opinion, colon + constructor (that is, something else than type) will be much more misleading to newcomers:
given c: C with {...}
What is C with {...} ? a type ? Versus:
given c extends C {...}
Now it is regular and familiar. But, as in the anonymous case we can’t write:
It turns out what I called “constructor inference” above already works:
class A
val a: A = new {}
It is mentioned here under the name “naked new”. I think it is not put forward because we want to get rid of new with creator applications. However, it has traits in common with the given with syntax:
They both break the rule that (keyword, operator) starts an indented block and : starts a template body.
They both mandate braces if an empty block is wanted
naked new together with given alias can be seen as an alternative to given with:
given C = new {...}
given C with {...}
So, my main prevention against the given with syntax as a disgracious exception vanishes, as it is consistent with at least one other construct in the language. In consequence, I will stop trying to prevent it in RC1. I still think my alternative proposal is worth giving a try, but it could be done later under the regular SIP process.