@@ -230,7 +230,7 @@ Named patterns are compatible with extensible pattern matching simply because
230
230
231
231
### Operations on Named Tuples
232
232
233
- The operations on named tuples are defined in object ` scala.NamedTuple ` . The current version of this object is listed in the appendix .
233
+ The operations on named tuples are defined in object ` scala.NamedTuple ` . The current version of this object is listed in Appendix A .
234
234
235
235
### Restrictions
236
236
@@ -250,6 +250,13 @@ The following restrictions apply to named tuples and named pattern arguments:
250
250
case (age = x) => // error
251
251
```
252
252
253
+ ### Use Case
254
+
255
+ As a a use case showing some advanced capabilities of named tuples (including computed field names and the `From` type ),
256
+ we show an implementation of embedded queries in Scala . For expressions that look like working with collections are instead
257
+ used to directly generate a query AST that can be further optimized and mapped to a variety of query languages. The code
258
+ is given in Appendix B .
259
+
253
260
### Syntax Changes
254
261
255
262
The syntax of Scala is extended as follows to support named tuples and
@@ -346,7 +353,7 @@ This section should list prior work related to the proposal, notably:
346
353
347
354
## FAQ
348
355
349
- ## Appendix: NamedTuple Definition
356
+ ## Appendix A : NamedTuple Definition
350
357
351
358
Here is the current definition of ` NamedTuple ` . This is part of the library and therefore subject to future changes and additions.
352
359
@@ -544,4 +551,194 @@ object NamedTupleDecomposition:
544
551
/** The value types of a named tuple represented as a regular tuple. */
545
552
type DropNames [NT <: AnyNamedTuple ] <: Tuple = NT match
546
553
case NamedTuple [_, x] => x
554
+ ```
555
+
556
+ ## Appendix B: Embedded Queries Case Study
557
+
558
+ ``` scala
559
+ import language .experimental .namedTuples
560
+ import NamedTuple .{NamedTuple , AnyNamedTuple }
561
+
562
+ /* This is a demonstrator that shows how to map regular for expressions to
563
+ * internal data that can be optimized by a query engine. It needs NamedTuples
564
+ * and type classes but no macros. It's so far very provisional and experimental,
565
+ * intended as a basis for further exploration.
566
+ */
567
+
568
+ /** The type of expressions in the query language */
569
+ trait Expr [Result ] extends Selectable :
570
+
571
+ /** This type is used to support selection with any of the field names
572
+ * defined by Fields.
573
+ */
574
+ type Fields = NamedTuple .Map [NamedTuple .From [Result ], Expr ]
575
+
576
+ /** A selection of a field name defined by Fields is implemented by `selectDynamic`.
577
+ * The implementation will add a cast to the right Expr type corresponding
578
+ * to the field type.
579
+ */
580
+ def selectDynamic (fieldName : String ) = Expr .Select (this , fieldName)
581
+
582
+ /** Member methods to implement universal equality on Expr level. */
583
+ def == (other : Expr [? ]): Expr [Boolean ] = Expr .Eq (this , other)
584
+ def != (other : Expr [? ]): Expr [Boolean ] = Expr .Ne (this , other)
585
+
586
+ object Expr :
587
+
588
+ /** Sample extension methods for individual types */
589
+ extension (x : Expr [Int ])
590
+ def > (y : Expr [Int ]): Expr [Boolean ] = Gt (x, y)
591
+ def > (y : Int ): Expr [Boolean ] = Gt (x, IntLit (y))
592
+ extension (x : Expr [Boolean ])
593
+ def && (y : Expr [Boolean ]): Expr [Boolean ] = And (x, y)
594
+ def || (y : Expr [Boolean ]): Expr [Boolean ] = Or (x, y)
595
+
596
+ // Note: All field names of constructors in the query language are prefixed with `$`
597
+ // so that we don't accidentally pick a field name of a constructor class where we want
598
+ // a name in the domain model instead.
599
+
600
+ // Some sample constructors for Exprs
601
+ case class Gt ($x : Expr [Int ], $y : Expr [Int ]) extends Expr [Boolean ]
602
+ case class Plus (x : Expr [Int ], y : Expr [Int ]) extends Expr [Int ]
603
+ case class And ($x : Expr [Boolean ], $y : Expr [Boolean ]) extends Expr [Boolean ]
604
+ case class Or ($x : Expr [Boolean ], $y : Expr [Boolean ]) extends Expr [Boolean ]
605
+
606
+ // So far Select is weakly typed, so `selectDynamic` is easy to implement.
607
+ // Todo: Make it strongly typed like the other cases
608
+ case class Select [A ]($x : Expr [A ], $name : String ) extends Expr
609
+
610
+ case class Single [S <: String , A ]($x : Expr [A ])
611
+ extends Expr [NamedTuple [S *: EmptyTuple , A *: EmptyTuple ]]
612
+
613
+ case class Concat [A <: AnyNamedTuple , B <: AnyNamedTuple ]($x : Expr [A ], $y : Expr [B ])
614
+ extends Expr [NamedTuple .Concat [A , B ]]
615
+
616
+ case class Join [A <: AnyNamedTuple ](a : A )
617
+ extends Expr [NamedTuple .Map [A , StripExpr ]]
618
+
619
+ type StripExpr [E ] = E match
620
+ case Expr [b] => b
621
+
622
+ // Also weakly typed in the arguents since these two classes model universal equality */
623
+ case class Eq ($x : Expr [? ], $y : Expr [? ]) extends Expr [Boolean ]
624
+ case class Ne ($x : Expr [? ], $y : Expr [? ]) extends Expr [Boolean ]
625
+
626
+ /** References are placeholders for parameters */
627
+ private var refCount = 0
628
+
629
+ case class Ref [A ]($name : String = " " ) extends Expr [A ]:
630
+ val id = refCount
631
+ refCount += 1
632
+ override def toString = s " ref $id( ${$name}) "
633
+
634
+ /** Literals are type-specific, tailored to the types that the DB supports */
635
+ case class IntLit ($value : Int ) extends Expr [Int ]
636
+
637
+ /** Scala values can be lifted into literals by conversions */
638
+ given Conversion [Int , IntLit ] = IntLit (_)
639
+
640
+ /** The internal representation of a function `A => B`
641
+ * Query languages are ususally first-order, so Fun is not an Expr
642
+ */
643
+ case class Fun [A , B ](param : Ref [A ], f : B )
644
+
645
+ type Pred [A ] = Fun [A , Expr [Boolean ]]
646
+
647
+ /** Explicit conversion from
648
+ * (name_1: Expr[T_1], ..., name_n: Expr[T_n])
649
+ * to
650
+ * Expr[(name_1: T_1, ..., name_n: T_n)]
651
+ */
652
+ extension [A <: AnyNamedTuple ](x : A ) def toRow : Join [A ] = Join (x)
653
+
654
+ /** Same as _.toRow, as an implicit conversion */
655
+ given [A <: AnyNamedTuple ]: Conversion [A , Expr .Join [A ]] = Expr .Join (_)
656
+
657
+ end Expr
658
+
659
+ /** The type of database queries. So far, we have queries
660
+ * that represent whole DB tables and queries that reify
661
+ * for-expressions as data.
662
+ */
663
+ trait Query [A ]
664
+
665
+ object Query :
666
+ import Expr .{Pred , Fun , Ref }
667
+
668
+ case class Filter [A ]($q : Query [A ], $p : Pred [A ]) extends Query [A ]
669
+ case class Map [A , B ]($q : Query [A ], $f : Fun [A , Expr [B ]]) extends Query [B ]
670
+ case class FlatMap [A , B ]($q : Query [A ], $f : Fun [A , Query [B ]]) extends Query [B ]
671
+
672
+ // Extension methods to support for-expression syntax for queries
673
+ extension [R ](x : Query [R ])
674
+
675
+ def withFilter (p : Ref [R ] => Expr [Boolean ]): Query [R ] =
676
+ val ref = Ref [R ]()
677
+ Filter (x, Fun (ref, p(ref)))
678
+
679
+ def map [B ](f : Ref [R ] => Expr [B ]): Query [B ] =
680
+ val ref = Ref [R ]()
681
+ Map (x, Fun (ref, f(ref)))
682
+
683
+ def flatMap [B ](f : Ref [R ] => Query [B ]): Query [B ] =
684
+ val ref = Ref [R ]()
685
+ FlatMap (x, Fun (ref, f(ref)))
686
+ end Query
687
+
688
+ /** The type of query references to database tables */
689
+ case class Table [R ]($name : String ) extends Query [R ]
690
+
691
+ // Everything below is code using the model -----------------------------
692
+
693
+ // Some sample types
694
+ case class City (zipCode : Int , name : String , population : Int )
695
+ type Address = (city : City , street : String , number : Int )
696
+ type Person = (name : String , age : Int , addr : Address )
697
+
698
+ @ main def Test =
699
+
700
+ val cities = Table [City ](" cities" )
701
+
702
+ val q1 = cities.map: c =>
703
+ c.zipCode
704
+ val q2 = cities.withFilter: city =>
705
+ city.population > 10_000
706
+ .map: city =>
707
+ city.name
708
+
709
+ val q3 =
710
+ for
711
+ city <- cities
712
+ if city.population > 10_000
713
+ yield city.name
714
+
715
+ val q4 =
716
+ for
717
+ city <- cities
718
+ alt <- cities
719
+ if city.name == alt.name && city.zipCode != alt.zipCode
720
+ yield
721
+ city
722
+
723
+ val addresses = Table [Address ](" addresses" )
724
+ val q5 =
725
+ for
726
+ city <- cities
727
+ addr <- addresses
728
+ if addr.street == city.name
729
+ yield
730
+ (name = city.name, num = addr.number)
731
+
732
+ val q6 =
733
+ cities.map: city =>
734
+ (name = city.name, zipCode = city.zipCode)
735
+
736
+ def run [T ](q : Query [T ]): Iterator [T ] = ???
737
+
738
+ def x1 : Iterator [Int ] = run(q1)
739
+ def x2 : Iterator [String ] = run(q2)
740
+ def x3 : Iterator [String ] = run(q3)
741
+ def x4 : Iterator [City ] = run(q4)
742
+ def x5 : Iterator [(name : String , num : Int )] = run(q5)
743
+ def x6 : Iterator [(name : String , zipCode : Int )] = run(q6)
547
744
```
0 commit comments