Skip to content

Commit

Permalink
Update SIPs state
Browse files Browse the repository at this point in the history
  • Loading branch information
scala-improvement-bot committed Jan 20, 2024
1 parent b89c074 commit beea7fb
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 47 deletions.
90 changes: 45 additions & 45 deletions _sips/sips/42.type.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ their role is to give the meaning of paths selecting types and terms from nested
paths have an intuitive meaning to programmers from a wide range of backgrounds which belies their
underpinning by a somewhat "advanced" concept in type theory.

Nevertheless, by pairing a type with it's unique inhabitant, singleton types bridge the gap between
types and values, and their presence in Scala has over the years allowed Scala programmers to explore
techniques which would typically only be available in languages, such as Agda or Idris, with support
Nevertheless, by pairing a type with its unique inhabitant, singleton types bridge the gap between
types and values, and their presence in Scala has, over the years, allowed Scala programmers to explore
techniques which would typically only be available in languages such as Agda or Idris, with support
for full-spectrum dependent types.

Scala's semantics have up until now been richer than its syntax. The only singleton types which are
currently _directly_ expressible are those of the form `p.type` where `p` is a path pointing to a
value of some subtype of `AnyRef`. Internally the Scala compiler also represents singleton types for
individual values of subtypes of `AnyVal`, such as `Int` or values of type `String` which don't
individual values of subtypes of `AnyVal`, such as `Int` or values of type `String`, which don't
correspond to paths. These types are inferred in some circumstances, notably as the types of `final`
vals. Their primary purpose has been to represent compile time constants (see [6.24 Constant
vals. Their primary purpose has been to represent compile-time constants (see [6.24 Constant
Expressions](https://scala-lang.org/files/archive/spec/2.12/06-expressions.html#constant-expressions)
and the discussion of "constant value definitions" in [4.1 Value Declarations and
Definitions](https://scala-lang.org/files/archive/spec/2.12/04-basic-declarations-and-definitions.html#value-declarations-and-definitions)).
Expand Down Expand Up @@ -89,15 +89,15 @@ Lightbend Scala compiler.
foo(1: 1) // type ascription
```

+ The `.type` singleton type forming operator can be applied to values of all subtypes of `Any`.
To prevent the compiler from widening our return type we assign to a final val.
+ The `.type` singleton-type-forming operator can be applied to values of all subtypes of `Any`.
To prevent the compiler from widening our return type, we assign to a final val.
```
def foo[T](t: T): t.type = t
final val bar = foo(23) // result is bar: 23
```

+ The presence of an upper bound of `Singleton` on a formal type parameter indicates that
singleton types should be inferred for type parameters at call sites. To help see this
singleton types should be inferred for type parameters at call sites. To help see this,
we introduce type constructor `Id` to prevent the compiler from widening our return type.
```
type Id[A] = A
Expand All @@ -118,7 +118,7 @@ Lightbend Scala compiler.
```

+ A `scala.ValueOf[T]` type class and corresponding `scala.Predef.valueOf[T]` operator has been
added yielding the unique value of types with a single inhabitant.
added, yielding the unique value of types with a single inhabitant.
```
def foo[T](implicit v: ValueOf[T]): T = v.value
foo[13] // result is 13: Int
Expand All @@ -129,13 +129,13 @@ Lightbend Scala compiler.

Many of the examples below use primitives provided by the Scala generic programming library
[shapeless](https://github.com/milessabin/shapeless/). It provides a `Witness` type class and a
family of Scala macro based methods and conversions for working with singleton types and shifting
family of Scala-macro-based methods and conversions for working with singleton types and shifting
from the value to the type level and vice versa. One of the goals of this SIP is to enable Scala
programmers to achieve similar results without having to rely on a third party library or fragile
and non-portable macros.

The relevant parts of shapeless are excerpted in [Appendix 1](#appendix-1--shapeless-excerpts).
Given the definitions there, some of forms summarized above can be expressed in current Scala,
Given the definitions there, some of the forms summarized above can be expressed in current Scala,
```
val wOne = Witness(1)
val one: wOne.T = wOne.value // wOne.T is the type 1
Expand All @@ -147,13 +147,13 @@ foo[wOne.T] // result is 1: 1
"foo" ->> 23 // shapeless record field constructor
// result type is FieldType["foo", Int]
```
The syntax is awkward and hiding it from library users is challenging. Nevertheless they enable many
The syntax is awkward, and hiding it from library users is challenging. Nevertheless they enable many
constructs which have proven valuable in practice.

#### shapeless records

shapeless models records as HLists (essentially nested pairs) of record values with their types
tagged with the singleton types of their keys. The library provides user friendly mechanisms for
tagged with the singleton types of their keys. The library provides user-friendly mechanisms for
constructing record _values_, however it is extremely laborious to express the corresponding _types_.
Consider the following record value,
```
Expand All @@ -165,7 +165,7 @@ val book =
HNil
```

Using shapeless and current Scala the following would be required to give `book` an explicit type
Using shapeless and current Scala, the following would be required to give `book` an explicit type
annotation,
```
val wAuthor = Witness("author")
Expand Down Expand Up @@ -241,20 +241,20 @@ val c: Int Refined Greater[w6.T] = a
^
```

Under this proposal we can express these refinements much more succinctly,
Under this proposal, we can express these refinements much more succinctly,
```
val a: Int Refined Greater[5] = 10
val b: Int Refined Greater[4] = a
```

Type level predicates of this kind have proved to be useful in practice and are supported by modules
Type-level predicates of this kind have proved to be useful in practice and are supported by modules
of a [number of important libraries](https://github.com/fthomas/refined#external-modules).

Experience with those libraries has led to a desire to compute directly over singleton types, in
effect to lift whole term-level expressions to the type-level which has resulted in the development
effect to lift whole term-level expressions to the type level, which has resulted in the development
of the [singleton-ops](https://github.com/fthomas/singleton-ops) library. singleton-ops is built
with Typelevel Scala which allows it to use literal types as discussed in this SIP.
with Typelevel Scala, which allows it to use literal types, as discussed in this SIP.

```
import singleton.ops._
Expand All @@ -279,7 +279,7 @@ singleton-ops is used by a number of libraries, most notably our next motivating

[Libra](https://github.com/to-ithaca/libra) is a a dimensional analysis library based on shapeless,
spire and singleton-ops. It support SI units at the type level for all numeric types. Like
singleton-ops Libra is built using Typelevel Scala and so is able to use literal types as discussed
singleton-ops, Libra is built using Typelevel Scala and so is able to use literal types, as discussed
in this SIP.

Libra allows numeric computations to be checked for dimensional correctness as follows,
Expand Down Expand Up @@ -324,7 +324,7 @@ case class Residue[M <: Int](n: Int) extends AnyVal {
}
```

Given this definition we can work with modular numbers without any danger of mixing numbers with
Given this definition, we can work with modular numbers without any danger of mixing numbers with
different moduli,

```
Expand All @@ -342,7 +342,7 @@ fiveModTen + fourModEleven
```

Also note that the use of `ValueOf` as an implicit argument of `+` means that the modulus does not
need to be stored along with the `Int` in the `Residue` value which could be beneficial in
need to be stored along with the `Int` in the `Residue` value, which could be beneficial in
applications which work with large datasets.

### Proposal details
Expand All @@ -360,15 +360,15 @@ applications which work with large datasets.
| ‘(’ Types ‘)’
```

Examples,
Examples:
```
val one: 1 = 1 // val declaration
def foo(x: 1): Option[1] = Some(x) // param type, type arg
def bar[T <: 1](t: T): T = t // type parameter bound
foo(1: 1) // type ascription
```

+ The restriction that the singleton type forming operator `.type` can only be appended to
+ The restriction that the singleton-type-forming operator `.type` can only be appended to
stable paths designating a value which conforms to `AnyRef` is dropped -- the path may now conform
to `Any`. Section
[3.2.1](https://scala-lang.org/files/archive/spec/2.12/03-types.html#singleton-types) of the SLS is
Expand All @@ -385,7 +385,7 @@ applications which work with large datasets.
> denoted by `p` (i.e., the value `v` for which `v eq p`). Where the path does not conform to
> `scala.AnyRef` the type denotes the set consisting of only the value denoted by `p`.
Example,
Example:
```
def foo[T](t: T): t.type = t
final val bar = foo(23) // result is bar: 23
Expand Down Expand Up @@ -471,7 +471,7 @@ applications which work with large datasets.
> corresponding to a singleton-apt definition, or (2) The upper bound Ui of Ti conforms to
> `Singleton`.
Example,
Example:
```
type Id[A] = A
def wide[T](t: T): Id[T] = t
Expand All @@ -483,17 +483,17 @@ applications which work with large datasets.
Note that we introduce the type constructor `Id` simply to avoid widening of the return type.
+ A `scala.ValueOf[T]` type class and corresponding `scala.Predef.valueOf[T]` operator has been
added yielding the unique value of types with a single inhabitant.
added, yielding the unique value of types with a single inhabitant.
Type inference allows us to infer a singleton type from a literal value. It is natural to want to
be able to go in the other direction and infer a value from a singleton type. This latter
capability was exploited in the motivating `Residue` example given earlier, and is widely relied
on in current Scala in uses of shapeless's records, and `LabelledGeneric` based type class
on in current Scala in uses of shapeless's records, and `LabelledGeneric`-based type class
derivation.
Implicit resolution is Scala's mechanism for inferring values from types and in current Scala
Implicit resolution is Scala's mechanism for inferring values from types, and in current Scala,
shapeless provides a macro-based materializer for instances of its `Witness` type class. This SIP
adds a directly compiler supported type class as a replacement,
adds a directly compiler-supported type class as a replacement:
```
final class ValueOf[T](val value: T) extends AnyVal
Expand All @@ -502,20 +502,20 @@ applications which work with large datasets.
Instances are automatically provided for all types with a single inhabitant, which includes
literal and non-literal singleton types and `Unit`.
Example,
Example:
```
def foo[T](implicit v: ValueOf[T]): T = v.value
foo[13] // result is 13: Int
```
A method `valueOf` is also added to `scala.Predef` analogously to existing operators such as
A method `valueOf` is also added to `scala.Predef`, analogously to existing operators such as
`classOf`, `typeOf` etc.
```
def valueOf[T](implicit vt: ValueOf[T]): T = vt.value
```
Example,
Example:
```
object Foo
valueOf[Foo.type] // result is Foo: Foo.type
Expand All @@ -531,11 +531,11 @@ applications which work with large datasets.
where the `TypePat` is a literal type is translated as a match against the subsuming non-singleton
type followed by an equality test with the value corresponding to the literal type.
Where applied to literal types `isInstanceOf` is translated to a test against
Where applied to literal types, `isInstanceOf` is translated to a test against
the subsuming non-singleton type and an equality test with the value corresponding to the literal
type.
Examples,
Examples:
```
(1: Any) match {
case one: 1 => true
Expand All @@ -544,36 +544,36 @@ applications which work with large datasets.
(1: Any).isInstanceOf[1] // result is true: Boolean
```
Importantly, that doesn't include `asInstanceOf` as that is a user assertion to the compiler, with
Importantly, that doesn't include `asInstanceOf`, as that is a user assertion to the compiler, with
the compiler inserting in the generated code just enough code for the underlying runtime to not
give a `ValidationError`. The compiler should not, for instance, generate code such that an
expression like `(1: Any).asInstanceOf[2]` would throw a `ClassCastException`.
+ Default initialization for vars with literal types is forbidden.
The default initializer for a var is already mandated to be it's natural zero element (`0`,
`false`, `null` etc.). This is inconsistent with the var being given a non-zero literal type,
The default initializer for a var is already mandated to be its natural zero element (`0`,
`false`, `null` etc.). This is inconsistent with the var being given a non-zero literal type:
```
var bad: 1 = _
```
Whilst we could, in principle, provide an implicit non-default initializer for cases such as these
Whilst we could, in principle, provide an implicit non-default initializer for cases such as these,
it is the view of the authors of this SIP that there is nothing to be gained from enabling this
construction and that default initializer should be forbidden.
construction, and that default initializer should be forbidden.
## Follow on work from this SIP
## Follow-on work from this SIP
Whilst the authors of this SIP believe that it stands on its own merits, we think that there are two
areas where follow on work is desirable, and one area where another SIP might improve the implementation of SIP-23.
areas where follow-on work is desirable, and one area where another SIP might improve the implementation of SIP-23.
### Infix and prefix types
[SIP-33 Match Infix and Prefix Types to Meet Expression Rules](https://docs.scala-lang.org/sips/priority-based-infix-type-precedence.html)
has emerged from the work on refined types and computation over singleton types mentioned in the
motivation section above.
Once literal types are available it is natural to want to lift entire expressions to the type level
Once literal types are available, it is natural to want to lift entire expressions to the type level
as is done already in libraries such as [singleton-ops](https://github.com/fthomas/singleton-ops).
However, the precedence and associativity of symbolic infix _type constructors_ don't match the
precedence and associativity of symbolic infix _value operators_, and prefix type constructors don't
Expand All @@ -583,12 +583,12 @@ terms.
### Byte and short literals
`Byte` and `Short` have singleton types, but lack any corresponding syntax either at the type or at the term level.
These types are important in libraries which deal with low level numerics and protocol implementation
These types are important in libraries which deal with low-level numerics and protocol implementation
(see eg. [Spire](https://github.com/non/spire) and [Scodec](https://github.com/scodec/scodec)) and
elsewhere, and the ability to, for instance, index a type class by a byte or short literal would be
valuable.
A prototype of this syntax extension existed at an early stage in the development of Typelevel Scala
A prototype of this syntax extension existed at an early stage in the development of Typelevel Scala,
but never matured. The possibility of useful literal types adds impetus.
### Opaque types
Expand All @@ -610,7 +610,7 @@ would be elided, and the `valueOf[A]` method would be compiled to an identity fu
## Appendix 1 -- shapeless excerpts
Extracts from shapeless relevant to the motivating examples for this SIP,
Extracts from shapeless relevant to the motivating examples for this SIP:
```
trait Witness {
Expand Down
7 changes: 7 additions & 0 deletions _sips/sips/alternative-bind-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: SIP-60 - Alternative bind patterns
status: under-review
pull-request-number: 74
stage: design

---
7 changes: 7 additions & 0 deletions _sips/sips/multiple-assignments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: SIP-59 - Multiple assignments
status: under-review
pull-request-number: 73
stage: design

---
4 changes: 2 additions & 2 deletions _sips/sips/named-tuples.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: SIP-58 - Named Tuples
status: submitted
status: under-review
pull-request-number: 72
stage: pre-sip
stage: design

---

0 comments on commit beea7fb

Please sign in to comment.