From 455a6048f6c0e6a6729fbfe166d5c48072c2b690 Mon Sep 17 00:00:00 2001 From: Oron Port Date: Wed, 25 Oct 2017 18:25:53 +0300 Subject: [PATCH 01/10] Splitting SIP33 --- Gemfile.lock | 31 --- ...2-07-make-types-behave-like-expressions.md | 235 ------------------ 2 files changed, 266 deletions(-) delete mode 100644 _sips/sips/2017-02-07-make-types-behave-like-expressions.md diff --git a/Gemfile.lock b/Gemfile.lock index 8a52307428..61c9c6cb3d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,30 +1,11 @@ GEM remote: https://rubygems.org/ specs: - activesupport (5.1.2) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) - minitest (~> 5.1) - tzinfo (~> 1.1) addressable (2.5.1) public_suffix (~> 2.0, >= 2.0.2) colorator (1.1.0) - colored (1.2) - concurrent-ruby (1.0.5) - ethon (0.10.1) - ffi (>= 1.3.0) ffi (1.9.18) forwardable-extended (2.6.0) - html-proofer (3.7.2) - activesupport (>= 4.2, < 6.0) - addressable (~> 2.3) - colored (~> 1.2) - mercenary (~> 0.3.2) - nokogiri (~> 1.7) - parallel (~> 1.3) - typhoeus (~> 0.7) - yell (~> 2.0) - i18n (0.8.6) jekyll (3.5.1) addressable (~> 2.4) colorator (~> 1.0) @@ -48,11 +29,6 @@ GEM rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) mercenary (0.3.6) - mini_portile2 (2.2.0) - minitest (5.10.3) - nokogiri (1.8.0) - mini_portile2 (~> 2.2.0) - parallel (1.12.0) pathutil (0.14.0) forwardable-extended (~> 2.6) public_suffix (2.0.5) @@ -66,18 +42,11 @@ GEM sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - thread_safe (0.3.6) - typhoeus (0.8.0) - ethon (>= 0.8.0) - tzinfo (1.2.3) - thread_safe (~> 0.1) - yell (2.0.7) PLATFORMS ruby DEPENDENCIES - html-proofer jekyll-redirect-from BUNDLED WITH diff --git a/_sips/sips/2017-02-07-make-types-behave-like-expressions.md b/_sips/sips/2017-02-07-make-types-behave-like-expressions.md deleted file mode 100644 index 6b8e24b143..0000000000 --- a/_sips/sips/2017-02-07-make-types-behave-like-expressions.md +++ /dev/null @@ -1,235 +0,0 @@ ---- -layout: sip -discourse: true -title: SIP-33 - Match infix & prefix types to meet expression rules - -vote-status: pending -permalink: /sips/:title.html ---- - -**By: Oron Port** - -## History - -| Date | Version | -|---------------|------------------------------------------------------------------------| -| Feb 7th 2017 | Initial Draft | -| Feb 9th 2017 | Updates from feedback | -| Feb 10th 2017 | Updates from feedback | -| Aug 8th 2017 | Numbered SIP, improve view, fixed example, and added related issues | - - -Your feedback is welcome! If you're interested in discussing this proposal, head over to [this](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) Scala Contributors thread and let me know what you think. - ---- - -## Introduction -Currently scala allows symbol operators (`-`, `*`, `~~>`, etc.) for both type names and definition names. -Unfortunately, there is a 'surprise' element since the two differ in behaviour: - -### Infix operator precedence and associativity -Infix types are 'mostly' left-associative, -while the expression operation precedence is determined by the operator's first character (e.g., `/` is precedent to `+`). -Please see [Infix Types](http://scala-lang.org/files/archive/spec/2.12/03-types.html#infix-types) and [Infix Operations](http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#infix-operations) sections of the Scala specifications for more details. - -**Infix expression precedence vs. infix type precedence example**: - -```scala -object InfixExpressionPrecedence { - case class Nummy(expand : String) { - def + (that : Nummy) : Nummy = Nummy(s"Plus[$this,$that]") - def / (that : Nummy) : Nummy = Nummy(s"Div[$this,$that]") - } - object N1 extends Nummy("N1") - object N2 extends Nummy("N2") - object N3 extends Nummy("N3") - object N4 extends Nummy("N4") - //Both expand to Plus[Plus[N1,Div[N2,N3]],N4] - assert((N1 + N2 / N3 + N4).expand == (N1 + (N2 / N3) + N4).expand) -} -object InfixTypePrecedence { - trait Plus[N1, N2] - trait Div[N1, N2] - type +[N1, N2] = Plus[N1, N2] - type /[N1, N2] = Div[N1, N2] - trait N1 - trait N2 - trait N3 - trait N4 - //Error! - //Left expands to Plus[Div[Plus[N1,N2],N3],N4] (Surprising) - //Right expands to Plus[Plus[N1,Div[N2,N3]],N4] - implicitly[(N1 + N2 / N3 + N4) =:= (N1 + (N2 / N3) + N4)] -} -``` - -### Prefix operators bracketless unary use -While expressions have prefix unary operators, there are none for types. See the [Prefix Operations](http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#prefix-operations) section of the Scala specification. -This is a lacking feature of the type language Scala offers. See also interactions of this feature with other Scala features, further down this text. - - -**Prefix expression vs. prefix type example**: - -```scala -object PrefixExpression { - case class Nummy(expand : String) { - def unary_- : Nummy = Nummy(s"-$this") - def unary_~ : Nummy = Nummy(s"~$this") - def unary_! : Nummy = Nummy(s"!$this") - def unary_+ : Nummy = Nummy(s"+$this") - } - object N extends Nummy("N") - val n1 = -N - val n2 = ~N - val n3 = !N - val n4 = +N -} -object NonExistingPrefixTypes { - trait unary_-[A] - trait unary_~[A] - trait unary_![A] - trait unary_+[A] - trait N - type N1 = -N //Not working - type N2 = ~N //Not working - type N3 = !N //Not working - type N4 = +N //Not working -} -``` - ---- - -## Proposal -The proposal is split into two; type infix precedence, and prefix unary types. Note to the SIP committee: It might be better to vote on the two parts separately. - -### Proposal, Part 1: Infix type precedence & associativity -Make infix types conform to the same precedence and associativity traits as expression operations. - -### Proposal, Part 2: Prefix unary types -Add prefix types, exactly as specified for prefix expression. - - ---- - -## Motivation -The general motivation is developers expect terms and types to behave equally regarding operation precedence and availability of unary types. - -### Motivating examples - -#### Dotty infix type similarity -Dotty infix type associativity and precedence seem to act the same as expressions. -No documentation available to prove this, but the infix example above works perfectly in dotty. - -Dotty has no prefix types, same as Scalac. - -#### Singleton-ops library example -The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enables developers to express literal type operations more intuitively. -For example: - -```scala -import singleton.ops._ - -val four1 : 4 = implicitly[2 + 2] -val four2 : 2 + 2 = 4 -val four3 : 1 + 3 = implicitly[2 + 2] - -class MyVec[L] { - def doubleSize = new MyVec[2 * L] - def nSize[N] = new MyVec[N * L] -} -object MyVec { - implicit def apply[L](implicit check : Require[L > 0]) : MyVec[L] = new MyVec[L]() -} -val myVec : MyVec[10] = MyVec[4 + 1].doubleSize -val myBadVec = MyVec[-1] //fails compilation, as required -``` - -We currently loose some of the intuitive appeal due to the precedence issue: - -```scala -val works : 1 + (2 * 3) + 4 = 11 -val fails : 1 + 2 * 3 + 4 = 11 //left associative:(((1+2)*3)+4))) = 13 -``` - -#### Developer issues example -[This](http://stackoverflow.com/questions/23333882/scala-infix-type-aliasing-for-2-type-parameters) stackoverflow question demonstrate developers are 'surprised' by the difference in infix precedence, expecting infix type precedence to act the same as expression operations. - ---- - -## Interactions with other language features - -#### Variance Annotation -Variance annotation uses the `-` and `+` symbols to annotate contravariant and covariant subtyping, respectively. Introducing unary prefix types may lead to some developer confusion. -E.g. - -```scala -trait Negate[A] -trait Positive[A] -type unary_-[A] = Negate[A] -type unary_+[A] = Positive[A] -trait Contravariant[B, -A <: -B] //contravariant A subtype upper-bounded by Negate[B] -trait Covariant[B, +A <: +B] //covariant A subtype upper-bounded by Positive[B] -``` - -#### Negative Literal Types -Negative literal types are annotated using the `-` symbol. This can lead to the following confusion: - -```scala -trait Negate[A] -type unary_-[A] = Negate[A] -trait MyTrait[B] - -type MinusFortyTwo = MyTrait[-42] -type NegateFortyTwo = MyTrait[Negate[42]] -``` - -The above example demonstrates a case of two types `MinusFortyTwo` and `NegateFortyTwo` which are different. They may be equivalent in view (implicit conversion between the two type instances), but they are not equal. - -Note: It is not possible to annotate a positive literal type in Scala (checked both in TLS and Dotty): - -```scala -val a : 42 = +42 //works -val b : -42 = -42 //works -val c : +42 = 42 //error: ';' expected but integer literal found -``` - -This means that if unary prefix types are added, then `+42` will be a type expansion of `unary_+[42]`. - -**Related Issues** -* [Dotty Issue #2783](https://github.com/lampepfl/dotty/issues/2783) -* [Typelevel Scala Issue #157](https://github.com/typelevel/scala/issues/157) - -Both SIP23 implementation and Dotty's implementation of literal types currently fail compilation when infix types interact with a negative literal type. -```scala -type ~~[A, B] -type good = 2 ~~ 2 -type bad = 2 ~~ -2 //Error:(9, 20) ';' expected but integer literal found. -type work_around = 2 ~~ (-2) //works for Typelevel scala, but fails in Dotty -``` -It is not yet clear if this is an implementation issue, or if the spec should be changed to allow this as well. -If this is a spec change, then the committee should approve it also. - -#### Star `*` infix type interaction with repeated parameters -The [repeated argument symbol `*`](https://www.scala-lang.org/files/archive/spec/2.12/04-basic-declarations-and-definitions.html#repeated-parameters) may create confusion with the infix type `*`. -Please note that this feature interaction already exists within the current specification. - -```scala -trait *[N1, N2] -trait N1 -trait N2 -def foo(a : N1*N2*) : Unit = {} //repeated parameter of type *[N1, N2] -``` - -**Related Issues** -* [Dotty Issue #1961](https://github.com/lampepfl/dotty/issues/1961) - - -## Backward Compatibility -Changing infix type associativity and precedence affects code that uses type operations and conforms to the current specification. - ---- - -### Bibliography -[Scala Contributors](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) - -[scala-sips](https://groups.google.com/forum/#!topic/scala-sips/ARVf1RLDw9U) From 1eeb61246993e4cda833d0710d241969da56e3bb Mon Sep 17 00:00:00 2001 From: Oron Port Date: Wed, 25 Oct 2017 18:26:56 +0300 Subject: [PATCH 02/10] New SIP files --- ...07-priority-based-infix-type-precedence.md | 235 ++++++++++++++++++ _sips/sips/2017-10-25-adding-prefix-types.md | 165 ++++++++++++ _sips/sips/l.html | 228 +++++++++++++++++ 3 files changed, 628 insertions(+) create mode 100644 _sips/sips/2017-02-07-priority-based-infix-type-precedence.md create mode 100644 _sips/sips/2017-10-25-adding-prefix-types.md create mode 100644 _sips/sips/l.html diff --git a/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md new file mode 100644 index 0000000000..6b8e24b143 --- /dev/null +++ b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md @@ -0,0 +1,235 @@ +--- +layout: sip +discourse: true +title: SIP-33 - Match infix & prefix types to meet expression rules + +vote-status: pending +permalink: /sips/:title.html +--- + +**By: Oron Port** + +## History + +| Date | Version | +|---------------|------------------------------------------------------------------------| +| Feb 7th 2017 | Initial Draft | +| Feb 9th 2017 | Updates from feedback | +| Feb 10th 2017 | Updates from feedback | +| Aug 8th 2017 | Numbered SIP, improve view, fixed example, and added related issues | + + +Your feedback is welcome! If you're interested in discussing this proposal, head over to [this](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) Scala Contributors thread and let me know what you think. + +--- + +## Introduction +Currently scala allows symbol operators (`-`, `*`, `~~>`, etc.) for both type names and definition names. +Unfortunately, there is a 'surprise' element since the two differ in behaviour: + +### Infix operator precedence and associativity +Infix types are 'mostly' left-associative, +while the expression operation precedence is determined by the operator's first character (e.g., `/` is precedent to `+`). +Please see [Infix Types](http://scala-lang.org/files/archive/spec/2.12/03-types.html#infix-types) and [Infix Operations](http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#infix-operations) sections of the Scala specifications for more details. + +**Infix expression precedence vs. infix type precedence example**: + +```scala +object InfixExpressionPrecedence { + case class Nummy(expand : String) { + def + (that : Nummy) : Nummy = Nummy(s"Plus[$this,$that]") + def / (that : Nummy) : Nummy = Nummy(s"Div[$this,$that]") + } + object N1 extends Nummy("N1") + object N2 extends Nummy("N2") + object N3 extends Nummy("N3") + object N4 extends Nummy("N4") + //Both expand to Plus[Plus[N1,Div[N2,N3]],N4] + assert((N1 + N2 / N3 + N4).expand == (N1 + (N2 / N3) + N4).expand) +} +object InfixTypePrecedence { + trait Plus[N1, N2] + trait Div[N1, N2] + type +[N1, N2] = Plus[N1, N2] + type /[N1, N2] = Div[N1, N2] + trait N1 + trait N2 + trait N3 + trait N4 + //Error! + //Left expands to Plus[Div[Plus[N1,N2],N3],N4] (Surprising) + //Right expands to Plus[Plus[N1,Div[N2,N3]],N4] + implicitly[(N1 + N2 / N3 + N4) =:= (N1 + (N2 / N3) + N4)] +} +``` + +### Prefix operators bracketless unary use +While expressions have prefix unary operators, there are none for types. See the [Prefix Operations](http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#prefix-operations) section of the Scala specification. +This is a lacking feature of the type language Scala offers. See also interactions of this feature with other Scala features, further down this text. + + +**Prefix expression vs. prefix type example**: + +```scala +object PrefixExpression { + case class Nummy(expand : String) { + def unary_- : Nummy = Nummy(s"-$this") + def unary_~ : Nummy = Nummy(s"~$this") + def unary_! : Nummy = Nummy(s"!$this") + def unary_+ : Nummy = Nummy(s"+$this") + } + object N extends Nummy("N") + val n1 = -N + val n2 = ~N + val n3 = !N + val n4 = +N +} +object NonExistingPrefixTypes { + trait unary_-[A] + trait unary_~[A] + trait unary_![A] + trait unary_+[A] + trait N + type N1 = -N //Not working + type N2 = ~N //Not working + type N3 = !N //Not working + type N4 = +N //Not working +} +``` + +--- + +## Proposal +The proposal is split into two; type infix precedence, and prefix unary types. Note to the SIP committee: It might be better to vote on the two parts separately. + +### Proposal, Part 1: Infix type precedence & associativity +Make infix types conform to the same precedence and associativity traits as expression operations. + +### Proposal, Part 2: Prefix unary types +Add prefix types, exactly as specified for prefix expression. + + +--- + +## Motivation +The general motivation is developers expect terms and types to behave equally regarding operation precedence and availability of unary types. + +### Motivating examples + +#### Dotty infix type similarity +Dotty infix type associativity and precedence seem to act the same as expressions. +No documentation available to prove this, but the infix example above works perfectly in dotty. + +Dotty has no prefix types, same as Scalac. + +#### Singleton-ops library example +The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enables developers to express literal type operations more intuitively. +For example: + +```scala +import singleton.ops._ + +val four1 : 4 = implicitly[2 + 2] +val four2 : 2 + 2 = 4 +val four3 : 1 + 3 = implicitly[2 + 2] + +class MyVec[L] { + def doubleSize = new MyVec[2 * L] + def nSize[N] = new MyVec[N * L] +} +object MyVec { + implicit def apply[L](implicit check : Require[L > 0]) : MyVec[L] = new MyVec[L]() +} +val myVec : MyVec[10] = MyVec[4 + 1].doubleSize +val myBadVec = MyVec[-1] //fails compilation, as required +``` + +We currently loose some of the intuitive appeal due to the precedence issue: + +```scala +val works : 1 + (2 * 3) + 4 = 11 +val fails : 1 + 2 * 3 + 4 = 11 //left associative:(((1+2)*3)+4))) = 13 +``` + +#### Developer issues example +[This](http://stackoverflow.com/questions/23333882/scala-infix-type-aliasing-for-2-type-parameters) stackoverflow question demonstrate developers are 'surprised' by the difference in infix precedence, expecting infix type precedence to act the same as expression operations. + +--- + +## Interactions with other language features + +#### Variance Annotation +Variance annotation uses the `-` and `+` symbols to annotate contravariant and covariant subtyping, respectively. Introducing unary prefix types may lead to some developer confusion. +E.g. + +```scala +trait Negate[A] +trait Positive[A] +type unary_-[A] = Negate[A] +type unary_+[A] = Positive[A] +trait Contravariant[B, -A <: -B] //contravariant A subtype upper-bounded by Negate[B] +trait Covariant[B, +A <: +B] //covariant A subtype upper-bounded by Positive[B] +``` + +#### Negative Literal Types +Negative literal types are annotated using the `-` symbol. This can lead to the following confusion: + +```scala +trait Negate[A] +type unary_-[A] = Negate[A] +trait MyTrait[B] + +type MinusFortyTwo = MyTrait[-42] +type NegateFortyTwo = MyTrait[Negate[42]] +``` + +The above example demonstrates a case of two types `MinusFortyTwo` and `NegateFortyTwo` which are different. They may be equivalent in view (implicit conversion between the two type instances), but they are not equal. + +Note: It is not possible to annotate a positive literal type in Scala (checked both in TLS and Dotty): + +```scala +val a : 42 = +42 //works +val b : -42 = -42 //works +val c : +42 = 42 //error: ';' expected but integer literal found +``` + +This means that if unary prefix types are added, then `+42` will be a type expansion of `unary_+[42]`. + +**Related Issues** +* [Dotty Issue #2783](https://github.com/lampepfl/dotty/issues/2783) +* [Typelevel Scala Issue #157](https://github.com/typelevel/scala/issues/157) + +Both SIP23 implementation and Dotty's implementation of literal types currently fail compilation when infix types interact with a negative literal type. +```scala +type ~~[A, B] +type good = 2 ~~ 2 +type bad = 2 ~~ -2 //Error:(9, 20) ';' expected but integer literal found. +type work_around = 2 ~~ (-2) //works for Typelevel scala, but fails in Dotty +``` +It is not yet clear if this is an implementation issue, or if the spec should be changed to allow this as well. +If this is a spec change, then the committee should approve it also. + +#### Star `*` infix type interaction with repeated parameters +The [repeated argument symbol `*`](https://www.scala-lang.org/files/archive/spec/2.12/04-basic-declarations-and-definitions.html#repeated-parameters) may create confusion with the infix type `*`. +Please note that this feature interaction already exists within the current specification. + +```scala +trait *[N1, N2] +trait N1 +trait N2 +def foo(a : N1*N2*) : Unit = {} //repeated parameter of type *[N1, N2] +``` + +**Related Issues** +* [Dotty Issue #1961](https://github.com/lampepfl/dotty/issues/1961) + + +## Backward Compatibility +Changing infix type associativity and precedence affects code that uses type operations and conforms to the current specification. + +--- + +### Bibliography +[Scala Contributors](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) + +[scala-sips](https://groups.google.com/forum/#!topic/scala-sips/ARVf1RLDw9U) diff --git a/_sips/sips/2017-10-25-adding-prefix-types.md b/_sips/sips/2017-10-25-adding-prefix-types.md new file mode 100644 index 0000000000..330473ae1c --- /dev/null +++ b/_sips/sips/2017-10-25-adding-prefix-types.md @@ -0,0 +1,165 @@ +--- +layout: sip +discourse: true +title: SIP-NN - Adding prefix types + +vote-status: pending +permalink: /sips/:title.html +--- + +**By: Oron Port** + +## History + +| Date | Version | +| ------------- | ---------------------------------------- | +| Oct 25th 2017 | Split prefix types from [SIP33](http://docs.scala-lang.org/sips/priority-based-infix-type-precedence.html), and emphasize motivation | + + +Your feedback is welcome! If you're interested in discussing this proposal, head over to [this](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) Scala Contributors thread and let me know what you think. + +--- + +## Introduction +Currently scala supports unary prefix operators (`-`, `+`, `~`, `!`) for expressions (e.g., `def unary_-`) and does not support prefix types. See the Scala specification [Prefix Operations](http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#prefix-operations) section. + +**Prefix expression vs. prefix type example**: + +```scala +object PrefixExpression { + case class Nummy(expand : String) { + def unary_- : Nummy = Nummy(s"-$this") + def unary_~ : Nummy = Nummy(s"~$this") + def unary_! : Nummy = Nummy(s"!$this") + def unary_+ : Nummy = Nummy(s"+$this") + } + object N extends Nummy("N") + val n1 = -N + val n2 = ~N + val n3 = !N + val n4 = +N +} +object NonExistingPrefixTypes { + trait unary_-[A] + trait unary_~[A] + trait unary_![A] + trait unary_+[A] + trait N + type N1 = -N //Not working + type N2 = ~N //Not working + type N3 = !N //Not working + type N4 = +N //Not working +} +``` + +--- + +## Proposal +Add support for prefix types, which is equivalent to the prefix operations for expressions. + +``` +PrefixType ::= [`-' | `+' | `~' | `!'] SimpleType +CompoundType ::= PrefixType + | AnnotType {with AnnotType} [Refinement] + | Refinement +``` + +--- + +## Motivation +The general motivation is developers expect terms and types to behave the same. + +### Motivating examples + +The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enables developers to express literal type operations more intuitively. +For example: + +```scala +import singleton.ops._ + +object PrefixExample { + /* + We would much rather write the following to acheive more clarity and shorter code: + type Foo[Cond1, Cond2, Num] = ITE[Cond1 && !Cond2, -Num, Num] + */ + type Foo[Cond1, Cond2, Num] = ITE[Cond1 && ![Cond2], Negate[Num], Num] + def foo[Cond1, Cond2, Num](implicit f : Foo[Cond1, Cond2, Num]) : f.Out = f.value + def foo(cond1 : Boolean, cond2 : Boolean, num : Int) : Int = + if (cond1 && !cond2) -num else num +} + +import PrefixExample._ + +foo[true, false, 3] //returns -3 +foo(true, false, 3) //returns -3 +``` + +Note: `type ![A]` is possible to define, but `type -[A]` is not due to collision with infix type parsing. + +--- + +## Interactions with other language features + +#### Variance Annotation +Variance annotation uses the `-` and `+` symbols to annotate contravariant and covariant subtyping, respectively. Introducing unary prefix types may lead to some developer confusion. However, such interaction is very unlikely to occur. +E.g. + +```scala +trait Negate[A] +trait Positive[A] +type unary_-[A] = Negate[A] +type unary_+[A] = Positive[A] +trait Contravariant[B, -A <: +B] //contravariant A subtype upper-bounded by Positive[B] +trait Covariant[B, +A <: -B] //covariant A subtype upper-bounded by Negative[B] +``` + +#### Negative Literal Types +Negative literal types are annotated using the `-` symbol. This can lead to the following confusion: + +```scala +trait Negate[A] +type unary_-[A] = Negate[A] +trait MyTrait[B] + +type MinusFortyTwo = MyTrait[-42] +type NegateFortyTwo = MyTrait[Negate[42]] +``` + +The above example demonstrates a case of two types `MinusFortyTwo` and `NegateFortyTwo` which are different. They may be equivalent in view (implicit conversion between the two type instances), but they are not equal. + +Note: It is not possible to annotate a positive literal type in Scala (checked both in TLS and Dotty): + +```scala +val a : 42 = +42 //works +val b : -42 = -42 //works +val c : +42 = 42 //error: ';' expected but integer literal found +``` + +This means that if unary prefix types are added, then `+42` will be a type expansion of `unary_+[42]`. + +**Related Issues** +* [Dotty Issue #2783](https://github.com/lampepfl/dotty/issues/2783) +* [Typelevel Scala Issue #157](https://github.com/typelevel/scala/issues/157) + +Both SIP23 implementation and Dotty's implementation of literal types currently fail compilation when infix types interact with a negative literal type. +```scala +type ~~[A, B] +type good = 2 ~~ 2 +type bad = 2 ~~ -2 //Error:(9, 20) ';' expected but integer literal found. +type work_around = 2 ~~ (-2) //works for Typelevel scala, but fails in Dotty +``` +It is not yet clear if this is an implementation issue, or if the spec should be changed to allow this as well. +If this is a spec change, then the committee should approve it also. + +---- + +## Implementation + +A PR for this SIP is available at: + +--- + +### Bibliography +[Scala Contributors](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) + +[scala-sips](https://groups.google.com/forum/#!topic/scala-sips/ARVf1RLDw9U) diff --git a/_sips/sips/l.html b/_sips/sips/l.html new file mode 100644 index 0000000000..a2de7dbab7 --- /dev/null +++ b/_sips/sips/l.html @@ -0,0 +1,228 @@ +
+

layout: sip +discourse: true +title: SIP-NN - Match infix & prefix types to meet expression rules

+ +

vote-status: pending +permalink: /sips/:title.html +—

+ +

By: Oron Port

+ +

History

+ + + + + + + + + + + + + + + + + + + + + + +
DateVersion
Feb 7th 2017Initial Draft
Feb 9th 2017Updates from feedback
Feb 10th 2017Updates from feedback
+ +

Your feedback is welcome! If you’re interested in discussing this proposal, head over to this Scala Contributors thread and let me know what you think.

+ +
+ +

Introduction

+

Currently scala allows symbol operators (-, *, ~~>, etc.) for both type names and definition names. +Unfortunately, there is a ‘surprise’ element since the two differ in behaviour:

+ +

Infix operator precedence and associativity

+

Infix types are ‘mostly’ left-associative, +while the expression operation precedence is determined by the operator’s first character (e.g., / is precedent to +). +Please see Infix Types and Infix Operations sections of the Scala specifications for more details.

+ +

Example:

+ +
{% highlight scala %}
+object InfixExpressionPrecedence {
+  case class Nummy(expand : String) {
+    def + (that : Nummy) : Nummy = Nummy(s"Plus[$this,$that]")
+    def / (that : Nummy) : Nummy = Nummy(s"Div[$this,$that]")
+  }
+  object N1 extends Nummy("N1")
+  object N2 extends Nummy("N2")
+  object N3 extends Nummy("N3")
+  object N4 extends Nummy("N4")
+  //Both expand to Plus[Plus[N1,Div[N2,N3]],N4]
+  assert((N1 + N2 / N3 + N4).expand == (N1 + (N2 / N3) + N4).expand)
+}
+object InfixTypePrecedence {
+  trait Plus[N1, N2]
+  trait Div[N1, N2]
+  type +[N1, N2] = Plus[N1, N2]
+  type /[N1, N2] = Div[N1, N2]
+  trait N1
+  trait N2
+  trait N3
+  trait N4
+  //Error!
+  //Left  expands to Plus[Plus[N1,Div[N2,N3]],N4] (Surprising)
+  //Right expands to Plus[Div[Plus[N1,N2],N3],N4]
+  implicitly[(N1 + N2 / N3 + N4) =:= (N1 + (N2 / N3) + N4)]
+}
+{% endhighlight %}
+
+ +

Prefix operators bracketless unary use

+

While expressions have prefix unary operators, there are none for types. See the Prefix Operations section of the Scala specification. +This is a lacking feature of the type language Scala offers. See also interactions of this feature with other Scala features, further down this text.

+ +

Example:

+ +
{% highlight scala %}
+object PrefixExpression {
+  case class Nummy(expand : String) {
+    def unary_- : Nummy = Nummy(s"-$this")
+    def unary_~ : Nummy = Nummy(s"~$this")
+    def unary_! : Nummy = Nummy(s"!$this")
+    def unary_+ : Nummy = Nummy(s"+$this")
+  }
+  object N extends Nummy("N")
+  val n1 = -N
+  val n2 = ~N
+  val n3 = !N
+  val n4 = +N
+}
+object NonExistingPrefixTypes {
+  trait unary_-[A]
+  trait unary_~[A]
+  trait unary_![A]
+  trait unary_+[A]
+  trait N
+  type N1 = -N //Not working
+  type N2 = ~N //Not working
+  type N3 = !N //Not working
+  type N4 = +N //Not working
+}
+{% endhighlight %}
+
+ +
+ +

Proposal

+

The proposal is split into two; type infix precedence, and prefix unary types. Note to the SIP committee: It might be better to vote on the two parts separately.

+ +

Proposal, Part 1: Infix type precedence & associativity

+

Make infix types conform to the same precedence and associativity traits as expression operations.

+ +

Proposal, Part 2: Prefix unary types

+

Add prefix types, exactly as specified for prefix expression.

+ +
+ +

Motivation

+

The general motivation is developers expect terms and types to behave equally regarding operation precedence and availability of unary types.

+ +

Motivating examples

+ +

Dotty infix type similarity

+

Dotty infix type associativity and precedence seem to act the same as expressions. +No documentation available to prove this, but the infix example above works perfectly in dotty.

+ +

Dotty has no prefix types, same as Scalac.

+ +

Singleton-ops library example

+

The singleton-ops library with Typelevel Scala (which implemented SIP-23) enables developers to express literal type operations more intuitively. +For example:

+ +
{% highlight scala %}
+import singleton.ops._
+
+val four1 : 4 = implicitly[2 + 2]
+val four2 : 2 + 2 = 4
+val four3 : 1 + 3 = implicitly[2 + 2]
+
+class MyVec[L] {
+  def doubleSize = new MyVec[2 * L]
+  def nSize[N] = new MyVec[N * L]
+}
+object MyVec {
+  implicit def apply[L](implicit check : Require[L > 0]) : MyVec[L] = new MyVec[L]()
+}
+val myVec : MyVec[10] = MyVec[4 + 1].doubleSize
+val myBadVec = MyVec[-1] //fails compilation, as required
+{% endhighlight %}
+
+ +

We currently loose some of the intuitive appeal due to the precedence issue:

+ +
{% highlight scala %}
+val works : 1 + (2 * 3) + 4 = 11
+val fails : 1 + 2 * 3 + 4 = 11 //left associative:(((1+2)*3)+4))) = 13
+{% endhighlight %}
+
+ +

Developer issues example

+

This stackoverflow question demonstrate developers are ‘surprised’ by the difference in infix precedence, expecting infix type precedence to act the same as expression operations.

+ +
+ +

Interactions with other language features

+ +

Variance Annotation

+

Variance annotation uses the - and + symbols to annotate contravariant and covariant subtyping, respectively. Introducing unary prefix types may lead to some developer confusion. +E.g.

+ +
{% highlight scala %}
+trait Negate[A]
+trait Positive[A]
+type unary_-[A] = Negate[A]
+type unary_+[A] = Positive[A]
+trait Contravariant[B, -A <: -B] //contravariant A subtype upper-bounded by Negate[B]
+trait Covariant[B, +A <: +B] //covariant A subtype upper-bounded by Positive[B]
+{% endhighlight %}
+
+ +

Negative Literal Types

+

Negative literal types are annotated using the - symbol. This can lead to the following confusion:

+ +
{% highlight scala %}
+trait Negate[A]
+type unary_-[A] = Negate[A]
+trait MyTrait[B]
+
+type MinusFortyTwo = MyTrait[-42]
+type NegateFortyTwo = MyTrait[Negate[42]]
+{% endhighlight %}
+
+ +

The above example demonstrates a case of two types MinusFortyTwo and NegateFortyTwo which are different. They may be equivalent in view (implicit conversion between the two type instances), but they are not equal.

+ +

Note: It is not possible to annotate a positive literal type in Scala (checked both in TLS and Dotty):

+ +
{% highlight scala %}
+val a : 42 = +42 //works
+val b : -42 = -42 //works
+val c : +42 = 42 //error: ';' expected but integer literal found
+{% endhighlight %}
+
+ +

This means that if unary prefix types are added, then +42 will be a type expansion of unary_+[42].

+ +
+ +

Backward Compatibility

+

Changing infix type associativity and precedence affects code that uses type operations and conforms to the current specification.

+ +
+ +

Bibliography

+

Scala Contributors

+ +

scala-sips

From 5cc5971f263fa18243fe14ae1fe66a1b8b5d7aca Mon Sep 17 00:00:00 2001 From: Oron Port Date: Wed, 25 Oct 2017 19:19:47 +0300 Subject: [PATCH 03/10] Updates SIP 33 and new SIP-NN(prefix types) --- ...07-priority-based-infix-type-precedence.md | 133 +++--------------- _sips/sips/2017-10-25-adding-prefix-types.md | 22 ++- 2 files changed, 33 insertions(+), 122 deletions(-) diff --git a/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md index 5d502b725f..4cea984c10 100644 --- a/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md +++ b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md @@ -1,7 +1,7 @@ --- layout: sip discourse: true -title: SIP-33 - Match infix & prefix types to meet expression rules +title: SIP-33 - Priority-based infix type precedence vote-status: pending permalink: /sips/:title.html @@ -11,13 +11,14 @@ permalink: /sips/:title.html ## History -| Date | Version | -|---------------|------------------------------------------------------------------------| -| Feb 7th 2017 | Initial Draft | -| Feb 9th 2017 | Updates from feedback | -| Feb 10th 2017 | Updates from feedback | -| Aug 8th 2017 | Numbered SIP, improve view, fixed example, and added related issues | -| Oct 20th 2017 | Added implementation link | +| Date | Version | +| ------------- | ---------------------------------------- | +| Feb 7th 2017 | Initial Draft | +| Feb 9th 2017 | Updates from feedback | +| Feb 10th 2017 | Updates from feedback | +| Aug 8th 2017 | Numbered SIP, improve view, fixed example, and added related issues | +| Oct 20th 2017 | Added implementation link | +| Oct 25th 2017 | Moved prefix types to [another SIP](http://docs.scala-lang.org/sips/adding-prefix-types.html), changed title and PR | Your feedback is welcome! If you're interested in discussing this proposal, head over to [this](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) Scala Contributors thread and let me know what you think. @@ -26,12 +27,7 @@ Your feedback is welcome! If you're interested in discussing this proposal, head ## Introduction Currently scala allows symbol operators (`-`, `*`, `~~>`, etc.) for both type names and definition names. -Unfortunately, there is a 'surprise' element since the two differ in behaviour: - -### Infix operator precedence and associativity -Infix types are 'mostly' left-associative, -while the expression operation precedence is determined by the operator's first character (e.g., `/` is precedent to `+`). -Please see [Infix Types](http://scala-lang.org/files/archive/spec/2.12/03-types.html#infix-types) and [Infix Operations](http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#infix-operations) sections of the Scala specifications for more details. +Unfortunately, there is a 'surprise' element since the two differ in behavior. While infix types are 'mostly' left-associative, the expression operation precedence is determined by the operator's first character (e.g., `/` is precedent to `+`). Please see [Infix Types](http://scala-lang.org/files/archive/spec/2.12/03-types.html#infix-types) and [Infix Operations](http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#infix-operations) sections of the Scala specifications for more details. **Infix expression precedence vs. infix type precedence example**: @@ -64,56 +60,16 @@ object InfixTypePrecedence { } ``` -### Prefix operators bracketless unary use -While expressions have prefix unary operators, there are none for types. See the [Prefix Operations](http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#prefix-operations) section of the Scala specification. -This is a lacking feature of the type language Scala offers. See also interactions of this feature with other Scala features, further down this text. - - -**Prefix expression vs. prefix type example**: - -```scala -object PrefixExpression { - case class Nummy(expand : String) { - def unary_- : Nummy = Nummy(s"-$this") - def unary_~ : Nummy = Nummy(s"~$this") - def unary_! : Nummy = Nummy(s"!$this") - def unary_+ : Nummy = Nummy(s"+$this") - } - object N extends Nummy("N") - val n1 = -N - val n2 = ~N - val n3 = !N - val n4 = +N -} -object NonExistingPrefixTypes { - trait unary_-[A] - trait unary_~[A] - trait unary_![A] - trait unary_+[A] - trait N - type N1 = -N //Not working - type N2 = ~N //Not working - type N3 = !N //Not working - type N4 = +N //Not working -} -``` - --- ## Proposal -The proposal is split into two; type infix precedence, and prefix unary types. Note to the SIP committee: It might be better to vote on the two parts separately. -### Proposal, Part 1: Infix type precedence & associativity Make infix types conform to the same precedence and associativity traits as expression operations. -### Proposal, Part 2: Prefix unary types -Add prefix types, exactly as specified for prefix expression. - - --- ## Motivation -The general motivation is developers expect terms and types to behave equally regarding operation precedence and availability of unary types. +Developers expect terms and types to be expressed the same for mathematical and logical operations. ### Motivating examples @@ -124,8 +80,7 @@ No documentation available to prove this, but the infix example above works perf Dotty has no prefix types, same as Scalac. #### Singleton-ops library example -The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enables developers to express literal type operations more intuitively. -For example: +The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enable developers to express literal type operations more intuitively. For example: ```scala import singleton.ops._ @@ -157,79 +112,37 @@ val fails : 1 + 2 * 3 + 4 = 11 //left associative:(((1+2)*3)+4))) = 13 --- -## Interactions with other language features - -#### Variance Annotation -Variance annotation uses the `-` and `+` symbols to annotate contravariant and covariant subtyping, respectively. Introducing unary prefix types may lead to some developer confusion. -E.g. - -```scala -trait Negate[A] -trait Positive[A] -type unary_-[A] = Negate[A] -type unary_+[A] = Positive[A] -trait Contravariant[B, -A <: -B] //contravariant A subtype upper-bounded by Negate[B] -trait Covariant[B, +A <: +B] //covariant A subtype upper-bounded by Positive[B] -``` - -#### Negative Literal Types -Negative literal types are annotated using the `-` symbol. This can lead to the following confusion: - -```scala -trait Negate[A] -type unary_-[A] = Negate[A] -trait MyTrait[B] - -type MinusFortyTwo = MyTrait[-42] -type NegateFortyTwo = MyTrait[Negate[42]] -``` - -The above example demonstrates a case of two types `MinusFortyTwo` and `NegateFortyTwo` which are different. They may be equivalent in view (implicit conversion between the two type instances), but they are not equal. - -Note: It is not possible to annotate a positive literal type in Scala (checked both in TLS and Dotty): +## Implementation -```scala -val a : 42 = +42 //works -val b : -42 = -42 //works -val c : +42 = 42 //error: ';' expected but integer literal found -``` +A PR for this SIP is available at: -This means that if unary prefix types are added, then `+42` will be a type expansion of `unary_+[42]`. +------ -**Related Issues** -* [Dotty Issue #2783](https://github.com/lampepfl/dotty/issues/2783) -* [Typelevel Scala Issue #157](https://github.com/typelevel/scala/issues/157) +### Interactions with other language features -Both SIP23 implementation and Dotty's implementation of literal types currently fail compilation when infix types interact with a negative literal type. -```scala -type ~~[A, B] -type good = 2 ~~ 2 -type bad = 2 ~~ -2 //Error:(9, 20) ';' expected but integer literal found. -type work_around = 2 ~~ (-2) //works for Typelevel scala, but fails in Dotty -``` -It is not yet clear if this is an implementation issue, or if the spec should be changed to allow this as well. -If this is a spec change, then the committee should approve it also. - #### Star `*` infix type interaction with repeated parameters The [repeated argument symbol `*`](https://www.scala-lang.org/files/archive/spec/2.12/04-basic-declarations-and-definitions.html#repeated-parameters) may create confusion with the infix type `*`. Please note that this feature interaction already exists within the current specification. ```scala +trait +[N1, N2] trait *[N1, N2] trait N1 trait N2 -def foo(a : N1*N2*) : Unit = {} //repeated parameter of type *[N1, N2] +def foo(a : N1*N1+N2*) : Unit = {} //repeated parameter of type +[*[N1, N1], N2] ``` +However, it is very unlikely that such interaction would occur. + **Related Issues** + * [Dotty Issue #1961](https://github.com/lampepfl/dotty/issues/1961) ## Backward Compatibility -Changing infix type associativity and precedence affects code that uses type operations and conforms to the current specification. +Changing infix type associativity and precedence affects code that uses type operations and conforms to the current specification. -## Implementation -Pull request is available at https://github.com/scala/scala/pull/6142 +Note: changing the infix precedence didn't fail any scalac test. --- diff --git a/_sips/sips/2017-10-25-adding-prefix-types.md b/_sips/sips/2017-10-25-adding-prefix-types.md index 330473ae1c..02729e8d17 100644 --- a/_sips/sips/2017-10-25-adding-prefix-types.md +++ b/_sips/sips/2017-10-25-adding-prefix-types.md @@ -67,12 +67,11 @@ CompoundType ::= PrefixType --- ## Motivation -The general motivation is developers expect terms and types to behave the same. +Developers expect terms and types to be expressed the same for mathematical and logical operations. ### Motivating examples -The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enables developers to express literal type operations more intuitively. -For example: +The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enable developers to express literal type operations more intuitively. For example: ```scala import singleton.ops._ @@ -98,11 +97,16 @@ Note: `type ![A]` is possible to define, but `type -[A]` is not due to collision --- -## Interactions with other language features +## Implementation + +A PR for this SIP is available at: + +------ + +### Interactions with other language features #### Variance Annotation -Variance annotation uses the `-` and `+` symbols to annotate contravariant and covariant subtyping, respectively. Introducing unary prefix types may lead to some developer confusion. However, such interaction is very unlikely to occur. -E.g. +Variance annotation uses the `-` and `+` symbols to annotate contravariant and covariant subtyping, respectively. Introducing unary prefix types may lead to some developer confusion. However, such interaction is very unlikely to occur. E.g.: ```scala trait Negate[A] @@ -153,12 +157,6 @@ If this is a spec change, then the committee should approve it also. ---- -## Implementation - -A PR for this SIP is available at: - ---- - ### Bibliography [Scala Contributors](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) From 010ec45967d473e727eb146d76d2dad2e0df70e9 Mon Sep 17 00:00:00 2001 From: Oron Port Date: Thu, 26 Oct 2017 00:37:28 +0300 Subject: [PATCH 04/10] Added updated PR links --- _sips/sips/2017-02-07-priority-based-infix-type-precedence.md | 2 +- _sips/sips/2017-10-25-adding-prefix-types.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md index 4cea984c10..36431d52a8 100644 --- a/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md +++ b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md @@ -114,7 +114,7 @@ val fails : 1 + 2 * 3 + 4 = 11 //left associative:(((1+2)*3)+4))) = 13 ## Implementation -A PR for this SIP is available at: +A PR for this SIP is available at: [https://github.com/scala/scala/pull/6147](https://github.com/scala/scala/pull/6147) ------ diff --git a/_sips/sips/2017-10-25-adding-prefix-types.md b/_sips/sips/2017-10-25-adding-prefix-types.md index 02729e8d17..417f35b170 100644 --- a/_sips/sips/2017-10-25-adding-prefix-types.md +++ b/_sips/sips/2017-10-25-adding-prefix-types.md @@ -99,7 +99,7 @@ Note: `type ![A]` is possible to define, but `type -[A]` is not due to collision ## Implementation -A PR for this SIP is available at: +A PR for this SIP is available at: [https://github.com/scala/scala/pull/6148](https://github.com/scala/scala/pull/6148) ------ From 97bddb172e537f5568ddb5c10ca7746c76d1571f Mon Sep 17 00:00:00 2001 From: Oron Port Date: Thu, 26 Oct 2017 00:41:41 +0300 Subject: [PATCH 05/10] redundant file --- _sips/sips/l.html | 228 ---------------------------------------------- 1 file changed, 228 deletions(-) delete mode 100644 _sips/sips/l.html diff --git a/_sips/sips/l.html b/_sips/sips/l.html deleted file mode 100644 index a2de7dbab7..0000000000 --- a/_sips/sips/l.html +++ /dev/null @@ -1,228 +0,0 @@ -
-

layout: sip -discourse: true -title: SIP-NN - Match infix & prefix types to meet expression rules

- -

vote-status: pending -permalink: /sips/:title.html -—

- -

By: Oron Port

- -

History

- - - - - - - - - - - - - - - - - - - - - - -
DateVersion
Feb 7th 2017Initial Draft
Feb 9th 2017Updates from feedback
Feb 10th 2017Updates from feedback
- -

Your feedback is welcome! If you’re interested in discussing this proposal, head over to this Scala Contributors thread and let me know what you think.

- -
- -

Introduction

-

Currently scala allows symbol operators (-, *, ~~>, etc.) for both type names and definition names. -Unfortunately, there is a ‘surprise’ element since the two differ in behaviour:

- -

Infix operator precedence and associativity

-

Infix types are ‘mostly’ left-associative, -while the expression operation precedence is determined by the operator’s first character (e.g., / is precedent to +). -Please see Infix Types and Infix Operations sections of the Scala specifications for more details.

- -

Example:

- -
{% highlight scala %}
-object InfixExpressionPrecedence {
-  case class Nummy(expand : String) {
-    def + (that : Nummy) : Nummy = Nummy(s"Plus[$this,$that]")
-    def / (that : Nummy) : Nummy = Nummy(s"Div[$this,$that]")
-  }
-  object N1 extends Nummy("N1")
-  object N2 extends Nummy("N2")
-  object N3 extends Nummy("N3")
-  object N4 extends Nummy("N4")
-  //Both expand to Plus[Plus[N1,Div[N2,N3]],N4]
-  assert((N1 + N2 / N3 + N4).expand == (N1 + (N2 / N3) + N4).expand)
-}
-object InfixTypePrecedence {
-  trait Plus[N1, N2]
-  trait Div[N1, N2]
-  type +[N1, N2] = Plus[N1, N2]
-  type /[N1, N2] = Div[N1, N2]
-  trait N1
-  trait N2
-  trait N3
-  trait N4
-  //Error!
-  //Left  expands to Plus[Plus[N1,Div[N2,N3]],N4] (Surprising)
-  //Right expands to Plus[Div[Plus[N1,N2],N3],N4]
-  implicitly[(N1 + N2 / N3 + N4) =:= (N1 + (N2 / N3) + N4)]
-}
-{% endhighlight %}
-
- -

Prefix operators bracketless unary use

-

While expressions have prefix unary operators, there are none for types. See the Prefix Operations section of the Scala specification. -This is a lacking feature of the type language Scala offers. See also interactions of this feature with other Scala features, further down this text.

- -

Example:

- -
{% highlight scala %}
-object PrefixExpression {
-  case class Nummy(expand : String) {
-    def unary_- : Nummy = Nummy(s"-$this")
-    def unary_~ : Nummy = Nummy(s"~$this")
-    def unary_! : Nummy = Nummy(s"!$this")
-    def unary_+ : Nummy = Nummy(s"+$this")
-  }
-  object N extends Nummy("N")
-  val n1 = -N
-  val n2 = ~N
-  val n3 = !N
-  val n4 = +N
-}
-object NonExistingPrefixTypes {
-  trait unary_-[A]
-  trait unary_~[A]
-  trait unary_![A]
-  trait unary_+[A]
-  trait N
-  type N1 = -N //Not working
-  type N2 = ~N //Not working
-  type N3 = !N //Not working
-  type N4 = +N //Not working
-}
-{% endhighlight %}
-
- -
- -

Proposal

-

The proposal is split into two; type infix precedence, and prefix unary types. Note to the SIP committee: It might be better to vote on the two parts separately.

- -

Proposal, Part 1: Infix type precedence & associativity

-

Make infix types conform to the same precedence and associativity traits as expression operations.

- -

Proposal, Part 2: Prefix unary types

-

Add prefix types, exactly as specified for prefix expression.

- -
- -

Motivation

-

The general motivation is developers expect terms and types to behave equally regarding operation precedence and availability of unary types.

- -

Motivating examples

- -

Dotty infix type similarity

-

Dotty infix type associativity and precedence seem to act the same as expressions. -No documentation available to prove this, but the infix example above works perfectly in dotty.

- -

Dotty has no prefix types, same as Scalac.

- -

Singleton-ops library example

-

The singleton-ops library with Typelevel Scala (which implemented SIP-23) enables developers to express literal type operations more intuitively. -For example:

- -
{% highlight scala %}
-import singleton.ops._
-
-val four1 : 4 = implicitly[2 + 2]
-val four2 : 2 + 2 = 4
-val four3 : 1 + 3 = implicitly[2 + 2]
-
-class MyVec[L] {
-  def doubleSize = new MyVec[2 * L]
-  def nSize[N] = new MyVec[N * L]
-}
-object MyVec {
-  implicit def apply[L](implicit check : Require[L > 0]) : MyVec[L] = new MyVec[L]()
-}
-val myVec : MyVec[10] = MyVec[4 + 1].doubleSize
-val myBadVec = MyVec[-1] //fails compilation, as required
-{% endhighlight %}
-
- -

We currently loose some of the intuitive appeal due to the precedence issue:

- -
{% highlight scala %}
-val works : 1 + (2 * 3) + 4 = 11
-val fails : 1 + 2 * 3 + 4 = 11 //left associative:(((1+2)*3)+4))) = 13
-{% endhighlight %}
-
- -

Developer issues example

-

This stackoverflow question demonstrate developers are ‘surprised’ by the difference in infix precedence, expecting infix type precedence to act the same as expression operations.

- -
- -

Interactions with other language features

- -

Variance Annotation

-

Variance annotation uses the - and + symbols to annotate contravariant and covariant subtyping, respectively. Introducing unary prefix types may lead to some developer confusion. -E.g.

- -
{% highlight scala %}
-trait Negate[A]
-trait Positive[A]
-type unary_-[A] = Negate[A]
-type unary_+[A] = Positive[A]
-trait Contravariant[B, -A <: -B] //contravariant A subtype upper-bounded by Negate[B]
-trait Covariant[B, +A <: +B] //covariant A subtype upper-bounded by Positive[B]
-{% endhighlight %}
-
- -

Negative Literal Types

-

Negative literal types are annotated using the - symbol. This can lead to the following confusion:

- -
{% highlight scala %}
-trait Negate[A]
-type unary_-[A] = Negate[A]
-trait MyTrait[B]
-
-type MinusFortyTwo = MyTrait[-42]
-type NegateFortyTwo = MyTrait[Negate[42]]
-{% endhighlight %}
-
- -

The above example demonstrates a case of two types MinusFortyTwo and NegateFortyTwo which are different. They may be equivalent in view (implicit conversion between the two type instances), but they are not equal.

- -

Note: It is not possible to annotate a positive literal type in Scala (checked both in TLS and Dotty):

- -
{% highlight scala %}
-val a : 42 = +42 //works
-val b : -42 = -42 //works
-val c : +42 = 42 //error: ';' expected but integer literal found
-{% endhighlight %}
-
- -

This means that if unary prefix types are added, then +42 will be a type expansion of unary_+[42].

- -
- -

Backward Compatibility

-

Changing infix type associativity and precedence affects code that uses type operations and conforms to the current specification.

- -
- -

Bibliography

-

Scala Contributors

- -

scala-sips

From 173b2fb90125240d7d732dd0a3af439c4f40ef34 Mon Sep 17 00:00:00 2001 From: Oron Port Date: Wed, 29 Nov 2017 15:03:33 +0200 Subject: [PATCH 06/10] Update to prefix types SIP, according to feedback on the PR. --- _sips/sips/2017-10-25-adding-prefix-types.md | 45 +++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/_sips/sips/2017-10-25-adding-prefix-types.md b/_sips/sips/2017-10-25-adding-prefix-types.md index 417f35b170..09e5498aac 100644 --- a/_sips/sips/2017-10-25-adding-prefix-types.md +++ b/_sips/sips/2017-10-25-adding-prefix-types.md @@ -14,6 +14,7 @@ permalink: /sips/:title.html | Date | Version | | ------------- | ---------------------------------------- | | Oct 25th 2017 | Split prefix types from [SIP33](http://docs.scala-lang.org/sips/priority-based-infix-type-precedence.html), and emphasize motivation | +| Nov 29th 2017 | Updated SIP according to feedback in the PR, and recent update to SIP23 | Your feedback is welcome! If you're interested in discussing this proposal, head over to [this](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) Scala Contributors thread and let me know what you think. @@ -54,24 +55,14 @@ object NonExistingPrefixTypes { --- -## Proposal -Add support for prefix types, which is equivalent to the prefix operations for expressions. - -``` -PrefixType ::= [`-' | `+' | `~' | `!'] SimpleType -CompoundType ::= PrefixType - | AnnotType {with AnnotType} [Refinement] - | Refinement -``` - ---- - ## Motivation -Developers expect terms and types to be expressed the same for mathematical and logical operations. +It is easier to reason about the language when mathematical and logical operations for both terms and types are expressed the same. The proposal is relevant solely for projects which utilize numeric literal type operations (supported by SIP23, which was not yet accepted into Lightbend Scala). However, the SIP is implementation is very small and should have minor effect on compiler performance. ### Motivating examples -The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enable developers to express literal type operations more intuitively. For example: +The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enable developers to express literal type operations more intuitively. + +Consider the following example, where `foo` has two equivalent implementations, one using types, while the other uses terms: ```scala import singleton.ops._ @@ -81,8 +72,10 @@ object PrefixExample { We would much rather write the following to acheive more clarity and shorter code: type Foo[Cond1, Cond2, Num] = ITE[Cond1 && !Cond2, -Num, Num] */ - type Foo[Cond1, Cond2, Num] = ITE[Cond1 && ![Cond2], Negate[Num], Num] + type Foo[Cond1, Cond2, Num] = ITE[Cond1 && ![Cond2], Negate[Num], Num] + //foo executes typelevel operations by using singleton-ops def foo[Cond1, Cond2, Num](implicit f : Foo[Cond1, Cond2, Num]) : f.Out = f.value + //foo executes term operations def foo(cond1 : Boolean, cond2 : Boolean, num : Int) : Int = if (cond1 && !cond2) -num else num } @@ -97,6 +90,19 @@ Note: `type ![A]` is possible to define, but `type -[A]` is not due to collision --- +## Proposal + +Add support for prefix types, which is equivalent to the prefix operations for expressions. + +``` +PrefixType ::= [`-' | `+' | `~' | `!'] SimpleType +CompoundType ::= PrefixType + | AnnotType {with AnnotType} [Refinement] + | Refinement +``` + +------ + ## Implementation A PR for this SIP is available at: [https://github.com/scala/scala/pull/6148](https://github.com/scala/scala/pull/6148) @@ -143,18 +149,15 @@ This means that if unary prefix types are added, then `+42` will be a type expan **Related Issues** * [Dotty Issue #2783](https://github.com/lampepfl/dotty/issues/2783) -* [Typelevel Scala Issue #157](https://github.com/typelevel/scala/issues/157) +* [Typelevel Scala Issue #157](https://github.com/typelevel/scala/issues/157) (Resolved in recent update to SIP23) -Both SIP23 implementation and Dotty's implementation of literal types currently fail compilation when infix types interact with a negative literal type. +Dotty's implementation of literal types currently fail compilation when infix types interact with a negative literal type. ```scala type ~~[A, B] type good = 2 ~~ 2 type bad = 2 ~~ -2 //Error:(9, 20) ';' expected but integer literal found. -type work_around = 2 ~~ (-2) //works for Typelevel scala, but fails in Dotty +type work_around = 2 ~~ (-2) //Error in Dotty ``` -It is not yet clear if this is an implementation issue, or if the spec should be changed to allow this as well. -If this is a spec change, then the committee should approve it also. - ---- ### Bibliography From 2174172077f4ce9ef676990becaea078112ba816 Mon Sep 17 00:00:00 2001 From: Oron Port Date: Wed, 29 Nov 2017 15:24:03 +0200 Subject: [PATCH 07/10] Update to SIP33 and added example to prefix types use --- ...07-priority-based-infix-type-precedence.md | 14 +++++----- _sips/sips/2017-10-25-adding-prefix-types.md | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md index 36431d52a8..a854735ce1 100644 --- a/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md +++ b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md @@ -62,14 +62,8 @@ object InfixTypePrecedence { --- -## Proposal - -Make infix types conform to the same precedence and associativity traits as expression operations. - ---- - ## Motivation -Developers expect terms and types to be expressed the same for mathematical and logical operations. +It is easier to reason about the language when mathematical and logical operations for both terms and types are expressed the same. ### Motivating examples @@ -112,6 +106,12 @@ val fails : 1 + 2 * 3 + 4 = 11 //left associative:(((1+2)*3)+4))) = 13 --- +## Proposal + +Make infix types conform to the same precedence and associativity traits as term operations. + +------ + ## Implementation A PR for this SIP is available at: [https://github.com/scala/scala/pull/6147](https://github.com/scala/scala/pull/6147) diff --git a/_sips/sips/2017-10-25-adding-prefix-types.md b/_sips/sips/2017-10-25-adding-prefix-types.md index 09e5498aac..2aee086860 100644 --- a/_sips/sips/2017-10-25-adding-prefix-types.md +++ b/_sips/sips/2017-10-25-adding-prefix-types.md @@ -60,6 +60,8 @@ It is easier to reason about the language when mathematical and logical operatio ### Motivating examples +#### Singleton-ops library example + The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enable developers to express literal type operations more intuitively. Consider the following example, where `foo` has two equivalent implementations, one using types, while the other uses terms: @@ -88,6 +90,30 @@ foo(true, false, 3) //returns -3 Note: `type ![A]` is possible to define, but `type -[A]` is not due to collision with infix type parsing. +#### DFiant library example + +DFiant is a domain specific language for hardware description I (Oron) am developing. Hardware interfaces have a direction annotation (e.g., `vec : DFBits[8] <> IN`). Sometime interfaces have multiple ports, in different directions. E.g.: + +```scala +trait MyInterface { + val data : DFBits[32] <> IN + val address : DFBits[32] <> OUT +} +``` + +To be able to use the same interface in reverse, we need the ability to easily express it as: + +```scala +trait MyReversableInterface[D <: Direction] { + val data : DFBits[32] <> D + val address : DFBits[32] <> ~D +} +``` + +An implicit conversion can then assure that `~IN` is translated to `OUT` and vice-versa. + +Interfaces may possess dozens of ports, thus without prefix types can be quite cumbersome. + --- ## Proposal From 9334cd28d6bebaaa6c7e886b352117ec58ba19e2 Mon Sep 17 00:00:00 2001 From: Oron Port Date: Wed, 29 Nov 2017 15:28:08 +0200 Subject: [PATCH 08/10] Minor grammer change. --- _sips/sips/2017-10-25-adding-prefix-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_sips/sips/2017-10-25-adding-prefix-types.md b/_sips/sips/2017-10-25-adding-prefix-types.md index 2aee086860..5185a8fcdd 100644 --- a/_sips/sips/2017-10-25-adding-prefix-types.md +++ b/_sips/sips/2017-10-25-adding-prefix-types.md @@ -56,7 +56,7 @@ object NonExistingPrefixTypes { --- ## Motivation -It is easier to reason about the language when mathematical and logical operations for both terms and types are expressed the same. The proposal is relevant solely for projects which utilize numeric literal type operations (supported by SIP23, which was not yet accepted into Lightbend Scala). However, the SIP is implementation is very small and should have minor effect on compiler performance. +It is easier to reason about the language when mathematical and logical operations for both terms and types are expressed the same. The proposal is relevant solely for projects which utilize numeric literal type operations (supported by SIP23, which was not yet accepted into Lightbend Scala). However, the SIP's implementation is very small and should have minor effect on compiler performance. ### Motivating examples From 513b47c95b744714100045e465552c679ec085e2 Mon Sep 17 00:00:00 2001 From: Oron Port Date: Wed, 29 Nov 2017 15:44:55 +0200 Subject: [PATCH 09/10] Update history table --- _sips/sips/2017-02-07-priority-based-infix-type-precedence.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md index a854735ce1..3b297d8e27 100644 --- a/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md +++ b/_sips/sips/2017-02-07-priority-based-infix-type-precedence.md @@ -19,6 +19,7 @@ permalink: /sips/:title.html | Aug 8th 2017 | Numbered SIP, improve view, fixed example, and added related issues | | Oct 20th 2017 | Added implementation link | | Oct 25th 2017 | Moved prefix types to [another SIP](http://docs.scala-lang.org/sips/adding-prefix-types.html), changed title and PR | +| Nov 29th 2017 | Updated SIP according to feedback in the PR | Your feedback is welcome! If you're interested in discussing this proposal, head over to [this](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) Scala Contributors thread and let me know what you think. From 262e4e15ad84e817161edc12204f7d2121715e57 Mon Sep 17 00:00:00 2001 From: soronpo Date: Fri, 1 Dec 2017 23:08:56 +0200 Subject: [PATCH 10/10] Added use-case for prefix type `~` --- _sips/sips/2017-10-25-adding-prefix-types.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_sips/sips/2017-10-25-adding-prefix-types.md b/_sips/sips/2017-10-25-adding-prefix-types.md index 5185a8fcdd..3bf656fd2e 100644 --- a/_sips/sips/2017-10-25-adding-prefix-types.md +++ b/_sips/sips/2017-10-25-adding-prefix-types.md @@ -15,6 +15,7 @@ permalink: /sips/:title.html | ------------- | ---------------------------------------- | | Oct 25th 2017 | Split prefix types from [SIP33](http://docs.scala-lang.org/sips/priority-based-infix-type-precedence.html), and emphasize motivation | | Nov 29th 2017 | Updated SIP according to feedback in the PR, and recent update to SIP23 | +| Dec 1st 2017 | Added use-case for prefix type `~` | Your feedback is welcome! If you're interested in discussing this proposal, head over to [this](https://contributors.scala-lang.org/t/sip-nn-make-infix-type-alias-precedence-like-expression-operator-precedence/471) Scala Contributors thread and let me know what you think. @@ -60,6 +61,9 @@ It is easier to reason about the language when mathematical and logical operatio ### Motivating examples +#### Splice prefix types for meta-programming +A requirement for `unary_~` is described by Martin Odersky at [this proposal](https://gist.github.com/odersky/f91362f6d9c58cc1db53f3f443311140). + #### Singleton-ops library example The [singleton-ops library](https://github.com/fthomas/singleton-ops) with [Typelevel Scala](https://github.com/typelevel/scala) (which implemented [SIP-23](http://docs.scala-lang.org/sips/pending/42.type.html)) enable developers to express literal type operations more intuitively.