Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change to given/using syntax #8162

Merged
merged 8 commits into from Feb 2, 2020
Merged

Conversation

@odersky
Copy link
Contributor

odersky commented Jan 31, 2020

This is a (hopefully final) tweak to the syntax of givens.

Thesis: The Status Quo:

The current (Dotty 0.21) implementation uses given in three roles

  • for instances
  • for parameter and arguments
  • for context functions

It was felt that this was too much. It's makes programs less readable since it is hard to see at a glance in what role a given is used. People voiced the concern whether we would not simply repeating the problems with overused implicits, just replacing implicit with given.

Antithesis: The Alternative

The alternative implemented in current master is to use

  • given for instances
  • infix with for context parameters
  • .with(...) for context arguments
  • ?=> for context functions

That nicely distinguishes different kinds of uses. But people felt that infix with had problems. Some usages read really nice with it but for others we run into problems of layout and possibly false ways to read it. One problem that was not voiced yet is this:

given [T] with Ord[T] as Ord[List[T]]

In colloquial usage A with B as C reads A with (B as C) but the definition above should be read as (A with B) as C instead.

Synthesis

We still want to distinguish uses of givens but want to avoid infix syntax for context parameters. So let's keep givens and context functions as in the alternative proposal but express context parameters and arguments with using (inside parens) instead. E.g.

given listOrd[T](using Ord[T]) as Ord[List[T]] { ... }

def max[T](x: T, y: T)(using Ord[T]): T = ...
max(xs, ys)(using listOrd)

This PR changes the docs according to the new syntax. To get started:

https://github.com/dotty-staging/dotty/blob/change-given-using/docs/docs/reference/contextual/motivation-new.md

@smarter

This comment has been minimized.

Copy link
Member

smarter commented Jan 31, 2020

This looks great to me, the only wrinkle is that 2.13 also has something called Using, which will make googling a bit harder: https://www.scala-lang.org/api/current/scala/util/Using$.html, I don't think that's a showstopper though.

maximum(xs).with(descending.with(listOrd.with(intOrd)))
maximum(xs)(using descending)
maximum(xs)(using descending(using listOrd))
maximum(xs)(using descending(using listOrd(using intOrd)))
```

## Multiple With Clauses

This comment has been minimized.

Copy link
@mzuehlke

mzuehlke Jan 31, 2020

Suggested change
## Multiple With Clauses
## Multiple Using Clauses
@@ -143,7 +143,7 @@ extension listOps on [T](xs: List[T]) {
def third: T = xs.tail.tail.head
}
extension on [T](xs: List[T]) with Ordering[T] {
extension on [T](xs: List[T])(using Ordering[T]) {

This comment has been minimized.

Copy link
@mzuehlke

mzuehlke Jan 31, 2020

Are these changes indented in the not new section ?

This comment has been minimized.

Copy link
@odersky

odersky Jan 31, 2020

Author Contributor

No they are not. Thank for pointing it out!

@nicolasstucki nicolasstucki added this to the 0.22 Tech Preview milestone Jan 31, 2020
@kavedaa

This comment has been minimized.

Copy link

kavedaa commented Jan 31, 2020

Big thumbs up for having context parameters in parentheses. This solves a lot of problems. using also solves conflict/ambituity in class constructors.

Btw the docs link isn't working.

@bmeesters

This comment has been minimized.

Copy link

bmeesters commented Jan 31, 2020

I find using with/using outside parenthesis aesthetically more pleasing, however if it makes advanced cases more ambiguous I am also in favor of doing it consistently within parenthesis. Other than that I was already in favor of all new changes to the implicit/given system. 👍

@SethTisue

This comment has been minimized.

Copy link
Member

SethTisue commented Jan 31, 2020

ditching infix 👍

using two different words in the two positions 👍

but given and using seem like synonyms to me. in English, we say "given X, then Y", or we might say "using X, you get Y". given is always an input, not an output. so it feels really strange to me — verging on downright wrong — that we would end up using given for the output, the "then" part.

(I'm well aware this is circling back to something previously suggested, but it wouldn't be the first such circling-back:)

what about give + given? give listOrd[T](given Ord[T]) seems very natural to me

@smarter

This comment has been minimized.

Copy link
Member

smarter commented Jan 31, 2020

give listOrd[T](given Ord[T]) seems very natural to me

I've always liked gift listOrd[T](given Ord[T]) (credit to @som-snytt for coming up with gift)

@Ichoran

This comment has been minimized.

Copy link

Ichoran commented Jan 31, 2020

That's a nice improvement!

This is the first time it's felt mostly acceptable to me, which given how critical I am, is some sort of non-negligible praise.

My remaining serious concern is that I don't think the given expressions read well out loud. Because "given" is used as an adjective, a preposition, or a noun, it's easy to start thinking about the wrong one and then end in a muddle before you get through the line of code, especially with the use of as to introduce the type.

Is it a noun? Well, elephant is a noun. Let's try that. And Bruce is a name. We can use that instead of listOrd. And Mayor is a type, so:

(an) elephant as Mayor
elephant Bruce as Mayor

Okay, that works, except it's just a subject (or object) of a sentence; it's just a sentence fragment. You'd expect something like

an elephant is Mayor
elephant Bruce is Mayor

Then we're all good: the sentence is over, we can understand it, breathe a sigh of relief, and move on. Otherwise it leaves you hanging.

Or is it an adjective, like medical?

medical as Mayor
medical Bruce as Mayor

Still leaves you hanging, and the adjective as type form is bizarre.

Or is it a preposition, like despite?

despite as Mayor
despite Bruce as Mayor

Prepositions link parts of sentences, so these forms--especially the Bruce-free form--feel especially weird and incomplete. Since it feels incomplete, it seems like it should open a block:

while (it is still dark)
  i will do various things
  that don't require me
  to see that well

despite (having) Bruce as Mayor
  I will do various things
  that don't require me
  to remember that Bruce is an elephant

Note here that Mayor includes all of the defs and stuff because it's acting as a noun, and all that is just various adjectives.

So the only interpretation that's marginally comfortable is the noun form, and even that doesn't work well as it's a sentence fragment.

So what's the problem? If it's the noun form we're using, we have no verb. is is a verb. = is a verb. as isn't. Having a verb allows elephant Bruce or just elephant to be the subject, and then the object can be List[Ord[T]] { ... }, and we have a complete sentence.

If it's some other form we're using, it's creating a very awkward construct at least in English, where the block structure doesn't match what the grammar suggests.

Note that in mathematics, the main usage of given is as a preposition, or in the closely related conjunction given that: "Given that x > 0, sqrt(x) is defined on the reals."

Now, the imperative verb form, give, doesn't have any of these problems:

give Bruce as a sacrifice

And note that the prepositional form is great where we have using now:

define foo
  from xs, some T's
  (given that we have an Ord[T])
  producing some more T's
  to be
    some stuff

So I think although we can get used to the current form, it's still not very natural. It's more of a struggle to understand because of how the usage clashes with standard grammar and/or standard usage in English.

So my least disruptive suggestion is to switch to verb form give, keep using as here, and just accept that imports will feel a little weird.

Overall I think it is very important not to make the main code feel weird in order to have less weird-feeling import statements.

import com.org.edu.orderings.{ _, using _ }

give listOrd[T] as List[Ord[T]] {
  def compare(ls: List[T], rs: List[T]) = ...
}

def second[T](xs: List[T])(using ord: List[Ord[T]]) = ...

second(foo)(using myCustomListOrd)

Is it a huge deal? No. But I think it will feel considerably more natural.

For a slightly larger change with more bikeshedding, provide has the advantage of sounding optional, matching usage, whereas when you give something it's not clear whether the recipient is required to take it. And since given already worked in argument position, we can save the higher-value identifier using (e.g. for resource usage, where it's already used) though I think using does feel even more natural; and we can use provide both globally to define parameters and locally to pass them:

import com.org.edu.orderings.{ _, provide _ }

provide listOrd[T] as List[Ord[T]] { ... }

def second[T](xs: List[T])(given ord: List[Ord[T]])

second(foo)(provide myCustomListOrd)

tl;dr

I think given / using is an improved pair, but given provokes awkward interpretations given standard English usage. give / using is better, and a minimal change. I think provide / given (or provide / using) is better yet.

@kavedaa

This comment has been minimized.

Copy link

kavedaa commented Jan 31, 2020

The problem with verbs such as give and provide is that many other Scala constructs, e.g. val, def, class are declarative and when read in real-world language can be prefixed with "let there be defined a", e.g. "let there be defined a class A with the following implementation".

You can't do that with give; however given is short for "given instance", hence you can read it as "let there be defined a given instance...".

Of course, a noun would be the ideal, but that has been elusive. It's easy to find nouns that work for typeclasses, but not for context passing, and certainly not for both. gift is the semantically closest (although I don't think that works very well either).

@som-snytt

This comment has been minimized.

Copy link
Contributor

som-snytt commented Jan 31, 2020

This is the first time it's felt mostly acceptable to me, which given how critical I am, is some sort of non-negligible praise.

Amen!

The other alternative which hasn't received due consideration is to use Chinese characters.

They are shorter than verbose English words and generally circumvent the grammatical conundrums that have plagued these discussions.

Also, why shouldn't Scala be the language that introduces Chinese characters to coders world-wide?

My spouse is Chinese, and when she's yelling at the daughter, I don't always understand what is at issue, but generally I am comfortable now with the writing system.

礼 is the simplified 禮 so probably for coding I would go with simplified.

I don't know what choices actual coders make. We have a team in Shanghai, but I don't have a chance to work with them.

Apparently Shanghai is expensive with respect to Bangalore? So probably there are other linguistic opportunities that are available.

The nice thing about the Chinese character is that it implies a ritual offering -- I was trying to remember that word, "offering", Bach's musikalisches Opfer is like Abraham and Isaac, a sacrifice.

A "given" is not merely a logical assumption, but a solemn commitment that may determine the course of an entire project.

I suppose one could also propose "offering", in that vein.

odersky added 2 commits Jan 31, 2020
This reverts commit 04095bc.

# Conflicts:
#	compiler/src/dotty/tools/dotc/parsing/Parsers.scala
#	compiler/src/dotty/tools/dotc/parsing/Tokens.scala
#	docs/docs/internals/syntax.md
#	tests/neg-macros/delegate-match-1/Macro_1.scala
#	tests/neg-macros/delegate-match-2/Macro_1.scala
#	tests/neg-macros/delegate-match-3/Macro_1.scala
#	tests/neg-macros/i6432/Macro_1.scala
#	tests/neg-macros/i6432b/Macro_1.scala
#	tests/neg-macros/i6976/Macro_1.scala
#	tests/neg-macros/inline-case-objects/Macro_1.scala
#	tests/neg-macros/inline-macro-staged-interpreter/Macro_1.scala
#	tests/neg-macros/inline-option/Macro_1.scala
#	tests/neg-macros/inline-tuples-1/Macro_1.scala
#	tests/neg-macros/macros-in-same-project-6/Foo.scala
#	tests/neg-macros/quote-error-2/Macro_1.scala
#	tests/neg-macros/quote-error/Macro_1.scala
#	tests/neg-macros/quote-exception/Macro_1.scala
#	tests/neg-macros/quote-whitebox/Macro_1.scala
#	tests/neg-macros/reflect-inline/assert_1.scala
#	tests/neg-macros/tasty-macro-assert-1/quoted_1.scala
#	tests/neg-macros/tasty-macro-assert-2/quoted_1.scala
#	tests/neg-macros/tasty-macro-error/quoted_1.scala
#	tests/neg-macros/tasty-macro-positions/quoted_1.scala
#	tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala
#	tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala
#	tests/neg/given-eta.scala
#	tests/neg/i7919.scala
#	tests/neg/implicit-params.scala
#	tests/pending/run/tasty-comments/quoted_1.scala
#	tests/pos-custom-args/erased/i7868.scala
#	tests/pos-macros/i6171/Macro_1.scala
#	tests/pos-macros/i6535/Macro_1.scala
#	tests/pos-macros/i6803b/Macro_1.scala
#	tests/pos-macros/i7011/Macros_1.scala
#	tests/pos-macros/quote-nested-object/Macro_1.scala
#	tests/pos-macros/quote-whitebox-2/Macro_1.scala
#	tests/pos-macros/tasty-constant-type/Macro_1.scala
#	tests/pos-macros/treemap-unapply/Macro.scala
#	tests/pos/i7204.scala
#	tests/pos/i7532.scala
#	tests/run-custom-args/Yretain-trees/tasty-definitions-2/Macro_1.scala
#	tests/run-custom-args/Yretain-trees/tasty-definitions-3/Macro_1.scala
#	tests/run-custom-args/Yretain-trees/tasty-extractors-owners/quoted_1.scala
#	tests/run-custom-args/Yretain-trees/tasty-load-tree-1/quoted_1.scala
#	tests/run-custom-args/Yretain-trees/tasty-load-tree-2/quoted_1.scala
#	tests/run-custom-args/run-macros-erased/reflect-isFunctionType/macro_1.scala
#	tests/run-macros/f-interpolation-1/FQuote_1.scala
#	tests/run-macros/gestalt-type-toolbox-reflect/Macro_1.scala
#	tests/run-macros/i4735/Macro_1.scala
#	tests/run-macros/i4803/Macro_1.scala
#	tests/run-macros/i4803b/Macro_1.scala
#	tests/run-macros/i4803c/Macro_1.scala
#	tests/run-macros/i5119/Macro_1.scala
#	tests/run-macros/i5119b/Macro_1.scala
#	tests/run-macros/i5188a/Macro_1.scala
#	tests/run-macros/i5533/Macro_1.scala
#	tests/run-macros/i5533b/Macro_1.scala
#	tests/run-macros/i5536/Macro_1.scala
#	tests/run-macros/i5629/Macro_1.scala
#	tests/run-macros/i5715/Macro_1.scala
#	tests/run-macros/i5941/macro_1.scala
#	tests/run-macros/i6171/Macro_1.scala
#	tests/run-macros/i6270/Macro_1.scala
#	tests/run-macros/i6518/Macro_1.scala
#	tests/run-macros/i6679/Macro_1.scala
#	tests/run-macros/i6765-c/Macro_1.scala
#	tests/run-macros/i6765/Macro_1.scala
#	tests/run-macros/i6988/FirstArg_1.scala
#	tests/run-macros/i7898/Macro_1.scala
#	tests/run-macros/i7964/Macro_1.scala
#	tests/run-macros/inferred-repeated-result/test_1.scala
#	tests/run-macros/inline-case-objects/Macro_1.scala
#	tests/run-macros/inline-macro-staged-interpreter/Macro_1.scala
#	tests/run-macros/inline-option/Macro_1.scala
#	tests/run-macros/inline-tuples-1/Macro_1.scala
#	tests/run-macros/inline-tuples-2/Macro_1.scala
#	tests/run-macros/inline-varargs-1/Macro_1.scala
#	tests/run-macros/quote-and-splice/Macros_1.scala
#	tests/run-macros/quote-inline-function/quoted_1.scala
#	tests/run-macros/quote-matcher-power/Macro_1.scala
#	tests/run-macros/quote-matcher-runtime/quoted_1.scala
#	tests/run-macros/quote-simple-macro/quoted_1.scala
#	tests/run-macros/quote-toExprOfTuple/Macro_1.scala
#	tests/run-macros/quote-type-matcher/quoted_1.scala
#	tests/run-macros/quote-whitebox/Macro_1.scala
#	tests/run-macros/quoted-expr-block/quoted_1.scala
#	tests/run-macros/quoted-matching-docs/Macro_1.scala
#	tests/run-macros/refined-selectable-macro/Macro_1.scala
#	tests/run-macros/reflect-dsl/assert_1.scala
#	tests/run-macros/reflect-inline/assert_1.scala
#	tests/run-macros/reflect-lambda/assert_1.scala
#	tests/run-macros/reflect-pos-fun/assert_1.scala
#	tests/run-macros/reflect-select-constructor/assert_1.scala
#	tests/run-macros/reflect-select-copy-2/assert_1.scala
#	tests/run-macros/reflect-select-copy/assert_1.scala
#	tests/run-macros/reflect-select-symbol-constructor/assert_1.scala
#	tests/run-macros/reflect-select-value-class/assert_1.scala
#	tests/run-macros/reflect-typeChecks/assert_1.scala
#	tests/run-macros/requiredSymbols/Macro_1.scala
#	tests/run-macros/tasty-argument-tree-1/quoted_1.scala
#	tests/run-macros/tasty-custom-show/quoted_1.scala
#	tests/run-macros/tasty-dealias/quoted_1.scala
#	tests/run-macros/tasty-definitions-1/quoted_1.scala
#	tests/run-macros/tasty-eval/quoted_1.scala
#	tests/run-macros/tasty-extractors-1/quoted_1.scala
#	tests/run-macros/tasty-extractors-2/quoted_1.scala
#	tests/run-macros/tasty-extractors-3/quoted_1.scala
#	tests/run-macros/tasty-extractors-types/quoted_1.scala
#	tests/run-macros/tasty-getfile/Macro_1.scala
#	tests/run-macros/tasty-interpolation-1/Macro.scala
#	tests/run-macros/tasty-linenumber-2/quoted_1.scala
#	tests/run-macros/tasty-linenumber/quoted_1.scala
#	tests/run-macros/tasty-location/quoted_1.scala
#	tests/run-macros/tasty-macro-assert/quoted_1.scala
#	tests/run-macros/tasty-macro-const/quoted_1.scala
#	tests/run-macros/tasty-macro-positions/quoted_1.scala
#	tests/run-macros/tasty-original-source/Macros_1.scala
#	tests/run-macros/tasty-seal-method/quoted_1.scala
#	tests/run-macros/tasty-simplified/quoted_1.scala
#	tests/run-macros/tasty-subtyping/quoted_1.scala
#	tests/run-macros/tasty-tree-map/quoted_1.scala
#	tests/run-macros/tasty-typeof/Macro_1.scala
#	tests/run-macros/tasty-unsafe-let/quoted_1.scala
#	tests/run-macros/type-show/Macro_1.scala
#	tests/run-macros/xml-interpolation-1/XmlQuote_1.scala
#	tests/run-macros/xml-interpolation-2/XmlQuote_1.scala
#	tests/run-macros/xml-interpolation-3/XmlQuote_1.scala
#	tests/run-with-compiler/i6201/macro_1.scala
#	tests/run/given-eta.scala
@morgen-peschke

This comment has been minimized.

Copy link

morgen-peschke commented Jan 31, 2020

The other alternative which hasn't received due consideration is to use Chinese characters.

While that would expand considerably the space of potential options, it might cause trouble with editors (or at least that's the justification given for avoiding non-ascii characters given by scalastyle)

@Ichoran

This comment has been minimized.

Copy link

Ichoran commented Jan 31, 2020

@kavedaa - You've just used a verb there, let. Also, def sounds like define to me, which is a verb. (I even gave an example with it as such.) import is also a verb.

Also, given instance isn't right, because "let there be defined a given instance listOrd[T](...)" is wrong, because it's not an instance, it's something that generates instances. Also, you can't really fith the as notation into that sentence structure without a lot of extra words.

Nouns don't work with as, anyway, as I illustrated with my example of the elephant. If it doesn't seem that way to you, that's worth noting, but you didn't mention that you noticed that specifically I argued against nouns, or provide a counterargument. You're assuming nouns are "of course" good, but I question that (with the current syntax).

@kavedaa

This comment has been minimized.

Copy link

kavedaa commented Jan 31, 2020

@kavedaa - You've just used a verb there, let.

You're nitpicking. :) It's hard form complete sentences without verbs. However, if we say "let there be defined a thing", thing is "that which is being defined/declared". The verb only appears in the real-world language explanation, in the programming language only the thing that is being defined/declared is written.

Also, def sounds like define to me, which is a verb. (I even gave an example with it as such.)

Or "a method definition".

import is also a verb.

Or "an import". Although, yes, I would read it as "do import".

Also, given instance isn't right, because "let there be defined a given instance listOrd[T](...)" is wrong, because it's not an instance, it's something that generates instances.

"Let there be defined, for any type T ... a given instance for Ord of List of T with the following implementation."

In other words, one instance for every, or any, type T.

(It's not quite precise, "generator of given instances" might be closer.)

Also, you can't really fith the as notation into that sentence structure without a lot of extra words.

I agree about as. In my opinion it should be for.

@Ichoran

This comment has been minimized.

Copy link

Ichoran commented Jan 31, 2020

@kavedaa - Grammar is all about nits!

Good point about for. I'll amend my previous comment to suggest that as an alternate minimal fix.

Er, wait, never mind. given Bruce for Mayor is using given not as a noun, though! It's using it as a preposition (read as, "given (Bruce for Mayor), ...").

I don't think it's really any better at avoiding the hanging-awaiting-the-rest-of-the-sentence feeling. It's just better because it denotes filling a role rather than being something which it isn't. (And Rust uses it: impl ThatTrait for SomeType { ... }).

@kavedaa

This comment has been minimized.

Copy link

kavedaa commented Jan 31, 2020

Er, wait, never mind. given Bruce for Mayor is using given not as a noun, though! It's using it as a preposition (read as, "given (Bruce for Mayor), ...").

In given intOrd for Ord[Int] it wouldn't be "a given intOrd", but "a given instance named intOrd" - i.e. "a given instance, named intOrd, for Ord of Int".

If we colloquially shorten "given instance" to "given", it would be "a given, named intOrd, for Ord of Int".

@som-snytt

This comment has been minimized.

Copy link
Contributor

som-snytt commented Jan 31, 2020

Now that Scala takes trailing commas, it should leverage more commas everywhere.

Comma could be the new semi-colon.

@odersky odersky force-pushed the dotty-staging:change-given-using branch from 8e02da9 to 9e86124 Jan 31, 2020
Convert all files that had a conflict in the last commit, which reverted
`with` clauses to `given` parameters.
@odersky odersky force-pushed the dotty-staging:change-given-using branch from 9e86124 to a18bc9e Jan 31, 2020
@Ichoran

This comment has been minimized.

Copy link

Ichoran commented Jan 31, 2020

@kavedaa - Okay, there exists a grammatically correct way to use for, but it's not the natural one.

My entire comment surrounds making the linguistic interpretation as simple as possible, so that the most natural reading suggests the right things.

@@ -3,7 +3,7 @@ import scala.quoted.matching._

inline def f: Any = ${ fImpl }

private def fImpl with (qctx: QuoteContext) : Expr[Unit] = {
private def fImpl (using qctx: QuoteContext) : Expr[Unit] = {

This comment has been minimized.

Copy link
@nicolasstucki

nicolasstucki Feb 1, 2020

Contributor

We should revert back to the version without extra spaces

private def fImpl(using qctx: QuoteContext): Expr[Unit] = {
@@ -7,6 +7,6 @@ object Bar {

inline def myMacro(): Unit = ${ aMacroImplementation }

def aMacroImplementation with QuoteContext : Expr[Unit] = '{}
def aMacroImplementation(given QuoteContext): Expr[Unit] = '{}

This comment has been minimized.

Copy link
@nicolasstucki

nicolasstucki Feb 1, 2020

Contributor

Should be

def aMacroImplementation(using QuoteContext): Expr[Unit] = '{}

same for other (given QuoteContext): is the tests.

@@ -2,7 +2,7 @@ class C { def f: Int = 0 }

trait D { def f(): Int = 0 }

def foo1(x: C & D) = x.f // error: method f must be called with argument
def foo1(x: C & D) = x.f // error: method f must be called(using ) argument

This comment has been minimized.

Copy link
@nicolasstucki

nicolasstucki Feb 1, 2020

Contributor
Suggested change
def foo1(x: C & D) = x.f // error: method f must be called(using ) argument
def foo1(x: C & D) = x.f // error: method f must be called with argument
@@ -12,7 +12,7 @@ object XmlQuote {
${XmlQuote.impl('ctx, 'args)}
}

def impl(receiver: Expr[StringContext], args: Expr[Seq[Any]]) with QuoteContext : Expr[Xml] = {
def impl(receiver: Expr[StringContext], args: Expr[Seq[Any]]) (using QuoteContext): Expr[Xml] = {

This comment has been minimized.

Copy link
@nicolasstucki

nicolasstucki Feb 1, 2020

Contributor
Suggested change
def impl(receiver: Expr[StringContext], args: Expr[Seq[Any]]) (using QuoteContext): Expr[Xml] = {
def impl(receiver: Expr[StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[Xml] = {

And other similar cases

@odersky

This comment has been minimized.

Copy link
Contributor Author

odersky commented Feb 1, 2020

@SethTisue @Ichoran It's true that a verb form can work better in some situations (even though given is also acceptable, as we all agree). But it does not work as well in others. Generally, we have at least three choice points: named/anonymous, instance/alias, conditional/unconditional. The verb works well only for named instances. Here's another typical use case for givens:

given Position = tree.pos

That reads OK: "the given position is tree.pos". But with a verb it would be

give Position = tree.pos

That does not work as well. It's pidgin english, and mixes terms and types in nonsensical ways.

After extensive experience, I have come to really like given. First it has exactly the right connotation. Context resolution is term inference. Given instances are literally the "givens" for this inference. "give" or "provide" do not have the same connotation. Second, given is deliciously malleable: it can be a noun, an adverb or an adjective. It can almost be a verb. At least it is not uncommon to start an exercise or proof with a long list of "given that the maximum speed is X, given that the distance is Y, etc" So while it does not make a complete sentence, even long partial sentences starting with given are quite common.

In short I have become quite comfortable with given for instances, and don't think we'll be able to do better.

@sideeffffect

This comment has been minimized.

Copy link
Contributor

sideeffffect commented Feb 1, 2020

👍 for splitting into 2 keywords, 1 for introduction, 1 for condition

But given belongs to the condition clause, not introduction/result, given the semantics of English.
There are many possibilities for introduction/result, even if we want to stick to noun:
https://www.wordhippo.com/what-is/the-noun-for/give.html
https://www.wordhippo.com/what-is/the-noun-for/provide.html
https://www.wordhippo.com/what-is/the-noun-for/offer.html

giving listOrd[T](given Ord[T]) as Ord[List[T]] { ... }
gift listOrd[T](given Ord[T]) as Ord[List[T]] { ... }
providing listOrd[T](given Ord[T]) as Ord[List[T]] { ... }
provider listOrd[T](given Ord[T]) as Ord[List[T]] { ... }
provision listOrd[T](given Ord[T]) as Ord[List[T]] { ... }
offer listOrd[T](given Ord[T]) as Ord[List[T]] { ... }
offering listOrd[T](given Ord[T]) as Ord[List[T]] { ... }
@amsayk

This comment has been minimized.

Copy link

amsayk commented Feb 1, 2020

Context has been used to describe implicits before so why not context/given:

context listOrd[T](given Ord[T]) as Ord[List[T]] { ... }

def max[T](x: T, y: T)(given Ord[T]): T = ...
max(xs, ys)(given listOrd)
context as ExecutionContext = ExecutionContext.global
context as Monad[Option] { ... }

and ?=> for context functions

@odersky odersky changed the title Change docs to given/using syntax Change to given/using syntax Feb 1, 2020
@odersky

This comment has been minimized.

Copy link
Contributor Author

odersky commented Feb 1, 2020

I plan on merging this tomorrow to make it into the release.

@Ichoran

This comment has been minimized.

Copy link

Ichoran commented Feb 1, 2020

@odersky - I'm not arguing that one can't get used to it. I expect one can, especially the given/using form.

I'm arguing that it given looks weird on first glance. provide String = "salmon" is in verb form. You just read it as 'provide (the String equal to "salmon")'. Noun form (used as an adjective) is fine too:
'((the) given String) equals "salmon"'. Verb form has the verb apply to the entire expression; noun form uses the noun as an adjective and binds to just the next symbol. Different parse trees, but both completely natural English.

What doesn't work so well is when you're using something that needs to be used as a noun, but it isn't obviously a noun when you encounter it the first time.

That's where the other uses favor verbs, or favor not using as. That's just weird, because you end up with a sentence fragment.

"Elephant Bruce as Mayor." This isn't really a sentence, is it? Even if you might find it on election materials.

"given bruce as Mayor { def laws = None }"

You have to stick in a bunch of extra words to rescue the grammar.

"let the given be bruce, which is defined as Mayor { def laws = None }"

If you use a verb, it works just fine, because it's just like "give chocolate as a present".

And if you use a word that has multiple grammatical uses and the initial parsing suggests the wrong form, you get exactly the same phenomenon in the trick phrase, "Fruit flies like a banana". It's hard to parse. (Indeed, in this case, there are two perfectly valid meanings depending on whether "flies" is a verb or a (pluralized) noun, though one of the two meanings is kind of weird.) Especially with as, I don't think you are using given as a noun even if you mean to. Prepositions are cool too, but sentence fragments not so much.

Anyway, I'm sure most everyone can eventually get used to it. I'm not sure if it might put off a substantial number of people without them realizing why, just having a feeling that it's "wrong".

Again, I think it's okay the way it is. As I said, the given/using pair is the first one that actually feels sufficiently comfortable to me. Once I'm used to it, it will be fine. But I think, inasmuch as natural language grammar is objective, that given produces objectively awkward constructs.

@amsayk - No thank you; that's a very weird form grammatically.

@jducoeur

This comment has been minimized.

Copy link
Contributor

jducoeur commented Feb 1, 2020

I suspect I'm going to lose this argument, but I have to protest again that you're overstating your claims of what does and does not work as English:

Here's another typical use case for givens:
given Position = tree.pos
That reads OK: "the given position is tree.pos". But with a verb it would be
give Position = tree.pos
That does not work as well. It's pidgin english, and mixes terms and types in nonsensical ways.

No, sorry -- that reads as "give the Position as tree.pos", which is decent, just different. Both versions require silently introducing additional parts of speech to read as English; both read adequately, if imperfectly. And "as" is a reasonable reading of =, which every English programmer I know pronounces as "equals" anyway, not "is". The choice of the one over the other isn't sacred.

Really, I think that what you are arguing for here as "English" is essentially a very specific dialect. I strongly suspect that your preference for the first form is because it is pronounced like a traditional mathematical lemma -- which it totally is, but I doubt that one programmer in a hundred actually finds that an intuitive way of thinking about their programs.

I'll say again (as a native speaker of English and amateur linguist for 30+ years) that the verbal variant communicates its meaning and intent more clearly to me, and your claims that one reads correctly as English and the other doesn't just aren't true. You're rationalizing a preference here, not arguing from linguistic truth, and I wish you'd stop claiming a linguistic high ground that you don't have here...

@Ichoran

This comment has been minimized.

Copy link

Ichoran commented Feb 1, 2020

@jducoeur - I think you're correct but you could be more polite about it :)

When you get used to a particular parsing of something, it's easy to argue from assuming that parse, not noticing that when you change forms an alternate parse is completely natural. (We generally design computer languages to not have this property, as it admits too much ambiguity, but in natural language it comes up all the time.)

So I think it's a very understandable mistake. But I agree it's a mistake. Both forms parse fine, just into different trees.

@jducoeur

This comment has been minimized.

Copy link
Contributor

jducoeur commented Feb 1, 2020

@jducoeur - I think you're correct but you could be more polite about it :)

Sorry -- currently sick and about to leave for a funeral. Apologies if that came off as too sharp.

@kavedaa

This comment has been minimized.

Copy link

kavedaa commented Feb 1, 2020

The curse and blessing of given is that it can be used in so many different forms. I count at least these:

  • action / verb: "it has been given"
  • modification / adjective: "a given x"
  • conditional / preposition: "given x, then y"
  • thing / noun: "a given" (i.e. a "sure thing")

So which form is it that is used in given as?

Consider this example:

"I will give him $10000 as compensation for his damages."
"Did you give him $10000?!"
"Yes, he was given $10000 as compensation."

Or more general:

"To give something as compensation."
"There was given something as compensation / as a gift / as blood money."

So as far as I can see, both give as and given as are verb forms, and more or less grammatically identical, aside from present/past tense.

(I'm not arguing for either form here, just trying to investigate. I think much of the discussion around given is due to people reading it in different ways.)

@Ichoran

This comment has been minimized.

Copy link

Ichoran commented Feb 2, 2020

@kavedaa - That's a good point. Note, however, that the was or has been is not optional. That is what distinguishes the verb form from the others. The "mood" used in programming languages is mostly imperative, which you can't do in past (or past progressive) tense.

@odersky

This comment has been minimized.

Copy link
Contributor Author

odersky commented Feb 2, 2020

given is very much on point meaningwise and it reads OK. I know that some of you are not excited about it, but in the end it seems to have the broadest support. So I propose we go with it. I want to leave the door open to changes for the following Dotty release period over the next 6 weeks. But unless something that has widespread consensus comes up during that period I believe we will stick with the current solution.

@odersky odersky merged commit c2478a3 into lampepfl:master Feb 2, 2020
2 checks passed
2 checks passed
CLA User signed CLA
Details
continuous-integration/drone/pr Build is passing
Details
@odersky odersky deleted the dotty-staging:change-given-using branch Feb 2, 2020
@Ichoran

This comment has been minimized.

Copy link

Ichoran commented Feb 4, 2020

I don't think the objection to the noun form of given was stated clearly until now, so I'm not sure it's possible to judge the level of carefully considered support.

But we can't debate this forever, so moving forward while allowing for an alternate consensus sounds reasonable to me.

@rkrzewski

This comment has been minimized.

Copy link

rkrzewski commented Feb 6, 2020

I'm really happy about the decision to use two separate keywords here, but I have a little suggestion: how about assume for introducing an implicit and given for consuming it?
We could then speak about introducing an implicit value as "making an assumption". An assumption may depend on other assumptions (assume x given y), one can make new assumptions locally that override global assumptions.
How does that check out English grammar-wise? Or maybe it sounds too mathematical, and will scare off pragmatic programmers? ;)

@kfish610

This comment has been minimized.

Copy link

kfish610 commented Feb 7, 2020

I have a different issue with this syntax. When I see

given listOrd[T](using ord: Ord[T]) as Ord[List[T]]

I read it as "given (a) listOrd (using ord as Ord[List[T]". It isn't really natural for me to associate the "as Ord[List[T]]" with the listOrd rather than the ord, even with parentheses. If I'm being honest, I think given made more sense than using because "given (a) listOrd given ord as Ord[List[T]]" doesn't sound like a sentence at all, so I can just read it like I read any other programming language. I think there needs to be a commitment to being language-like or programming language-like, not this weird in between thing that's happening.

@som-snytt

This comment has been minimized.

Copy link
Contributor

som-snytt commented Feb 7, 2020

Some viewpoints of course will fall squarely within the alternate consensus.

The difference between Scala and the Iowa caucus is that here the community has only a benevolent dictator to contend with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

You can’t perform that action at this time.