You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/_docs/reference/experimental/typeclasses.md
+10-10Lines changed: 10 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,7 +15,7 @@ A type class in Scala is a pattern where we define
15
15
- given instances at specific instantiations of that trait,
16
16
- using clauses or context bounds abstracting over that trait.
17
17
18
-
Type classes as a pattern work overall OK, but if we compare them to native implementations in Haskell, or protocols in Swift, or traits in Rust, then there are some idiosyncracies and rough corners which in the end make them
18
+
Type classes as a pattern work overall OK, but if we compare them to native implementations in Haskell, or protocols in Swift, or traits in Rust, then there are some idiosyncrasies and rough corners which in the end make them
19
19
a bit cumbersome and limiting for standard generic programming patterns. Much has improved since Scala 2's implicits, but there is still some gap to bridge to get to parity with these languages.
20
20
21
21
This note shows that with some fairly small and reasonable tweaks to Scala's syntax and typing rules we can obtain a much better scheme for working with type classes, or do generic programming in general.
@@ -67,7 +67,7 @@ requires that `Ordering` is a trait or class with a single type parameter (which
67
67
defflatMap[B](f: A=>Self[B]):Self[B]
68
68
defmap[B](f: A=>B) = x.flatMap(f `andThen` pure)
69
69
70
-
defreduce[A:Monoid](xs: List[A] =
70
+
defreduce[A:Monoid](xs: List[A]):A=
71
71
xs.foldLeft(Monoid.unit)(_ `combine` _)
72
72
73
73
traitParserCombinator:
@@ -86,7 +86,7 @@ requires that `Ordering` is a trait or class with a single type parameter (which
86
86
- Gives a clear indication of traits intended as type classes. A trait is a type class
87
87
if it has type `Self` as a member
88
88
- Allows to create aggregate type classes that combine givens via intersection types.
89
-
- Allows to use refinements in context bounds (the `combine` example above would be very awkward to express using the old way of context bounds expanding to type copnstructors).
89
+
- Allows to use refinements in context bounds (the `combine` example above would be very awkward to express using the old way of context bounds expanding to type constructors).
90
90
91
91
`Self`-based context bounds are a better fit for a dependently typed language like Scala than parameter-based ones. The main reason is that we are dealing with proper types, not type constructors. Proper types can be parameterized, intersected, or refined. This makes `Self`-based designs inherently more compositional than parameterized ones.
92
92
@@ -120,7 +120,7 @@ and not to:
120
120
121
121
Why not use `This` for the self type? The name `This` suggests that it is the type of `this`. But this is not true for type class traits. `Self` is the name of the type implementing a distinguished _member type_ of the trait in a `given` definition. `Self` is an established term in both Rust and Swift with the meaning used here.
122
122
123
-
One possible objection to the `Self` based design is that it does not cover "multi-parameter" type classes. But neither do context bounds! "Multi-parameter" type classes in Scala are simply givens that can be synthesized with the standard mechanisms. Type classes in the strict sense abstract only over a single type, namely the implementatation type of a trait.
123
+
One possible objection to the `Self` based design is that it does not cover "multi-parameter" type classes. But neither do context bounds! "Multi-parameter" type classes in Scala are simply givens that can be synthesized with the standard mechanisms. Type classes in the strict sense abstract only over a single type, namely the implementation type of a trait.
124
124
125
125
126
126
## Auxiliary Type Alias `is`
@@ -383,7 +383,7 @@ Here are some standard type classes, which were mostly already introduced at the
383
383
defunit:Self
384
384
385
385
traitFunctor:
386
-
typeSelf[A]
386
+
typeSelf[A]// Here, Self is a type constructor with parameter A
@@ -547,7 +547,7 @@ given [A: Combinator, B: Combinator { type Input = A.Input }]
547
547
y <- self.b.parse(in)
548
548
yield (x, y)
549
549
```
550
-
The example is now as expressed as straighforwardly as it should be:
550
+
The example is now as expressed as straightforwardly as it should be:
551
551
552
552
-`Combinator` is a type class with two associated types, `Input` and `Result`, and a `parse` method.
553
553
-`Apply` and `Combine` are two data constructors representing parser combinators. They are declared to be `Combinators` in the two subsequent `given` declarations.
@@ -567,12 +567,12 @@ _Note 2:_ One could improve the notation even further by adding equality constra
567
567
given [A:Combinator, B:CombinatorwithA.Input==B.Input]
568
568
=>Combine[A, B] is Combinator:
569
569
```
570
-
This variant is esthetically pleasing since it makes the equality constraint symmetric. The original version had to use an asymmetric refinement on the second type parameter bound instead. For now, such constraints are neither implemented nor proposed. This is left as a possibility for future work. Note also the analogy with
570
+
This variant is aesthetically pleasing since it makes the equality constraint symmetric. The original version had to use an asymmetric refinement on the second type parameter bound instead. For now, such constraints are neither implemented nor proposed. This is left as a possibility for future work. Note also the analogy with
571
571
the work of @mbovel and @Sporarum on refinement types, where similar `with` clauses can appear for term parameters. If that work goes ahead, we could possibly revisit the issue of `with` clauses also for type parameters.
572
572
573
573
### Example 4
574
574
575
-
Dimi Recordon tried to [port some core elenents](https://github.com/kyouko-taiga/scala-hylolib) of the type class based [Hylo standard library to Scala](https://github.com/hylo-lang/hylo/tree/main/StandardLibrary/Sources). It worked to some degree, but there were some things that could not be expressed, and more things that could be expressed only awkwardly.
575
+
Dimi Racordon tried to [port some core elements](https://github.com/kyouko-taiga/scala-hylolib) of the type class based [Hylo standard library to Scala](https://github.com/hylo-lang/hylo/tree/main/StandardLibrary/Sources). It worked to some degree, but there were some things that could not be expressed, and more things that could be expressed only awkwardly.
576
576
577
577
With the improvements proposed here, the library can now be expressed quite clearly and straightforwardly. See tests/pos/hylolib in this PR for details.
578
578
@@ -636,7 +636,7 @@ These would replace the previous syntax using `@`:
636
636
637
637
valxs@ (x :: xs1) = ys.checkedCast
638
638
```
639
-
**Advantages:** No unpronouncible and non-standard symbol like `@`. More regularity.
639
+
**Advantages:** No unpronounceable and non-standard symbol like `@`. More regularity.
640
640
641
641
Generally, we want to use `as name` to attach a name for some entity that could also have been used stand-alone.
642
642
@@ -668,5 +668,5 @@ type classes in Haskell, or with traits in Rust, or with protocols in Swift, or
668
668
The proposed scheme has similar expressiveness to Protocols in Swift or Traits in Rust. Both of these were largely influenced by Jeremy Siek's PdD thesis "[A language for generic programming](https://scholarworks.iu.edu/dspace/handle/2022/7067)", which was first proposed as a way to implement concepts in C++. C++ did not follow Siek's approach, but Swift and Rust did.
669
669
670
670
In Siek's thesis and in the formal treatments of Rust and Swift,
671
-
type class concepts are explained by mapping them to a lower level language of explicit dictionaries with representants for terms and types. Crucially, that lower level is not expressible without loss of granularity in the source language itself, since type representants are mapped to term dictionaries. By contrast, the current proposal expands type class concepts into other well-typed Scala constructs, which ultimately map into well-typed DOT programs. Type classes are simply a convenient notation for something that can already be expressed in Scala. In that sense, we stay true to the philosophy of a _scalable language_, where a small core can support a large range of advanced use cases.
671
+
type class concepts are explained by mapping them to a lower level language of explicit dictionaries with representations for terms and types. Crucially, that lower level is not expressible without loss of granularity in the source language itself, since type representations are mapped to term dictionaries. By contrast, the current proposal expands type class concepts into other well-typed Scala constructs, which ultimately map into well-typed DOT programs. Type classes are simply a convenient notation for something that can already be expressed in Scala. In that sense, we stay true to the philosophy of a _scalable language_, where a small core can support a large range of advanced use cases.
0 commit comments