@@ -217,9 +217,17 @@ object Expr:
217217 def isDefined : Expr [Boolean , S1 ] = Not (Expr .IsNull (x))
218218 def get : Expr [T , S1 ] = x.asInstanceOf [Expr [T , S1 ]] // TODO should this error silently?
219219 def getOrElse (default : Expr [T , S1 ]): Expr [T , S1 ] = coalesce(x.asInstanceOf [Expr [T , S1 ]], default)
220- def map [U : ResultTag , S2 <: ExprShape ](f : Ref [T , NonScalarExpr ] => Expr [U , NonScalarExpr ]): Expr [Option [U ], S1 ] =
220+ /* ABSTRACTION: To abstract over expressions (not relations) in the DSL, the DSL needs some kind of abstraction/application operation.
221+ Option 1: (already supported) use host-level abstraction e.g. define a lambda.
222+ Option 2: Use a macro to do substitution, but then lose the macro-free claim.
223+ Option 3: You could define an expression-level substitution, but since the case classes in this file use type parameters
224+ constrained by e.g. Numeric, it would be very annoying to write a type-safe substitution that replaces the expression reference
225+ by another expression in some larger expression.
226+ Because we do not want to use macros and the Option.map is expected to have little code, we pick the lambda.
227+ */
228+ def map [U ](f : Expr [T , NonScalarExpr ] => Expr [U , NonScalarExpr ])(using ResultTag [U ]): Expr [Option [U ], S1 ] =
221229 OptionMap (x, f)
222- // TODO unclear how to implement flatMap
230+
223231 // TODO somehow use options in aggregations
224232
225233 extension [S1 <: ExprShape ](x : Expr [Array [Byte ], S1 ])
@@ -333,31 +341,6 @@ object Expr:
333341 def lastValue [R , S <: ExprShape ](e : Expr [R , S ])(using ResultTag [R ]): ExprInWindowPosition [R ] = LastValue (e)
334342 def nthValue [R , S <: ExprShape ](e : Expr [R , S ], n : Int )(using ResultTag [R ]): ExprInWindowPosition [R ] = NthValue (e, n)
335343
336- // TODO aren't these types too restrictive?
337- def cases [T : ResultTag , SC <: ExprShape , SV <: ExprShape ]
338- (
339- firstCase : (Expr [Boolean , SC ] | true | ElseToken , Expr [T , SV ]),
340- restOfCases : (Expr [Boolean , SC ] | true | ElseToken , Expr [T , SV ])*
341- )
342- : Expr [T , SV ] =
343- var mainCases : collection.mutable.ArrayBuffer [(Expr [Boolean , SC ], Expr [T , SV ])] =
344- collection.mutable.ArrayBuffer .empty
345- var elseCase : Option [Expr [T , SV ]] = None
346- val cases = Seq (firstCase) ++ restOfCases
347- for (((condition, value), index) <- cases.zipWithIndex) {
348- condition match
349- case _ : ElseToken =>
350- assert(index == cases.size - 1 , " The default condition must be last" )
351- elseCase = Some (value)
352- case true =>
353- assert(index == cases.size - 1 , " The default condition must be last" )
354- elseCase = Some (value)
355- case false => assert(false , " what do you mean, false?" )
356- case _ : Expr [? , ? ] =>
357- mainCases += ((condition.asInstanceOf [Expr [Boolean , SC ]], value))
358- }
359- SearchedCase (mainCases.toList, elseCase)
360-
361344 // Note: All field names of constructors in the query language are prefixed with `$`
362345 // so that we don't accidentally pick a field name of a constructor class where we want
363346 // a name in the domain model instead.
@@ -551,7 +534,7 @@ object Expr:
551534 (using ResultTag [TE ], ResultTag [TR ]) extends Expr [TR , SR ]
552535
553536 case class OptionMap [A , B , S <: ExprShape ]
554- ($x : Expr [Option [A ], S ], $f : Ref [A , NonScalarExpr ] => Expr [B , NonScalarExpr ])
537+ ($x : Expr [Option [A ], S ], $f : Expr [A , NonScalarExpr ] => Expr [B , NonScalarExpr ])
555538 (using ResultTag [A ], ResultTag [B ]) extends Expr [Option [B ], S ]
556539
557540 case class Cast [A , B , S <: ExprShape ]($x : Expr [A , S ], resultType : CastTarget )(using ResultTag [B ]) extends Expr [B , S ]
@@ -595,7 +578,6 @@ object Expr:
595578 (using r : DialectFeature .RandomIntegerInInclusiveRange )
596579 : Expr [Int , CalculatedShape [S1 , S2 ]] =
597580 // TODO maybe add a check for (a <= b) if we know both components at generation time?
598- // TODO what about parentheses? Do we really not need them?
599581 RandomInt (a, b)
600582
601583 /** Should be able to rely on the implicit conversions, but not always. One approach is to overload, another is to
@@ -607,22 +589,6 @@ object Expr:
607589// case t:String => StringLit(t)
608590// case t:Boolean => BooleanLit(t)
609591
610- /* ABSTRACTION: if we want to abstract over expressions (not relations) in the DSL, to enable better composability,
611- then the DSL needs some kind of abstraction/application operation.
612- Option 1: (already supported) use host-level abstraction e.g. define a lambda.
613- Option 2: (below) define a substitution method, WIP.
614- Option 3: Use a macro to do substitution, but then lose the macro-free claim.
615- */
616- case class RefExpr [A : ResultTag , S <: ExprShape ]() extends Expr [A , S ]:
617- private val id = exprRefCount
618- exprRefCount += 1
619- def stringRef () = s " exprRef $id"
620- override def toString : String = s " ExprRef[ ${stringRef()}] "
621-
622- case class AbstractedExpr [A , B , S <: ExprShape ]($param : RefExpr [A , S ], $body : Expr [B , S ]):
623- def apply (exprArg : Expr [A , S ]): Expr [B , S ] =
624- substitute($body, $param, exprArg)
625- private def substitute [C ](expr : Expr [B , S ], formalP : RefExpr [A , S ], actualP : Expr [A , S ]): Expr [B , S ] = ???
626592 type Pred [A , S <: ExprShape ] = Fun [A , Expr [Boolean , S ], S ]
627593
628594 type IsTupleOfExpr [A <: AnyNamedTuple ] = Tuple .Union [NamedTuple .DropNames [A ]] <:< Expr [? , NonScalarExpr ]
@@ -644,6 +610,31 @@ object Expr:
644610
645611end Expr
646612
613+ // TODO aren't these types too restrictive?
614+ def cases [T : ResultTag , SC <: ExprShape , SV <: ExprShape ]
615+ (
616+ firstCase : (Expr [Boolean , SC ] | true | ElseToken , Expr [T , SV ]),
617+ restOfCases : (Expr [Boolean , SC ] | true | ElseToken , Expr [T , SV ])*
618+ )
619+ : Expr [T , SV ] =
620+ var mainCases : collection.mutable.ArrayBuffer [(Expr [Boolean , SC ], Expr [T , SV ])] =
621+ collection.mutable.ArrayBuffer .empty
622+ var elseCase : Option [Expr [T , SV ]] = None
623+ val cases = Seq (firstCase) ++ restOfCases
624+ for (((condition, value), index) <- cases.zipWithIndex) {
625+ condition match
626+ case _ : ElseToken =>
627+ assert(index == cases.size - 1 , " The default condition must be last" )
628+ elseCase = Some (value)
629+ case true =>
630+ assert(index == cases.size - 1 , " The default condition must be last" )
631+ elseCase = Some (value)
632+ case false => assert(false , " what do you mean, false?" )
633+ case _ : Expr [? , ? ] =>
634+ mainCases += ((condition.asInstanceOf [Expr [Boolean , SC ]], value))
635+ }
636+ Expr .SearchedCase (mainCases.toList, elseCase)
637+
647638inline def lit (x : () => java.io.InputStream ): Expr [() => java.io.InputStream , NonScalarExpr ] = Expr .ByteStreamLit (x)
648639inline def lit (x : java.io.InputStream ): Expr [() => java.io.InputStream , NonScalarExpr ] = Expr .ByteStreamLit (() => x)
649640inline def lit (x : Array [Byte ]): Expr [Array [Byte ], NonScalarExpr ] = Expr .BytesLit (x)
0 commit comments