WiseIndian / Coroutines-for-Scala-3

3 stars 2 forks source link

How to generate a match expression? #1

Closed WiseIndian closed 4 years ago

WiseIndian commented 4 years ago

I tried to generate the continue method of the Coroutine class. In our design we wanted that the Coroutine class implementations look something like this.

In order to generate the code of the continue method of the Coroutine class I experimented three possible ways this weekend. Sadly none compiled in the end.

The first way was this one:

I use a foldLeft in order to create the different cases of this match expression. At each iteration of the foldLeft I add a new case to the previous expressions (previous cases) encapsulated in the accumulator of the foldLeft.

      new Coroutine[T] { 
        var state: Int = 0 

        def continue: Option[T] = state match { ${

          (0 until nbFunDefs).foldLeft[Expr[_ <: Any]]( '{} ) {
            case (previousExpr, (funDef, index)) => 
              '{
                ${previousExpr} 
                case ${index} => f${index}()
              }
          }
        } }  

        ....
     }

In this case the compiler complains with this error:

[error] -- [E040] Syntax Error: /home/snif/hackerspace/semester/library/src/main/scala/Macros.scala:146:48 
[error] 146 |        def continue: Option[T] = state match { ${
[error]     |                                                                          ^
[error]     |                                 'case' expected, but identifier found

Although we're within a quote surrounding the code which create a "new Coroutine" we are not allowed to generate code with a splice inside the match.

Given that, I thought I could make use of the tasty API in order to go at a lower level of abstraction. So I wrote code like this:

        def continue: Option[T] = ${
          val caseDefs: List[CaseDef] = (0 until nbFunDefs).map[CaseDef] { 
            index => '{  case ${index} => f${index}() }.unseal //doesnt seem to work
          }

          Match('{state}.unseal, caseDefs).seal.cast[Option[T]] 
        } 

Sadly it seems that it is not possible to create a CaseDef in this way:

[error] -- [E040] Syntax Error: /home/snif/hackerspace/semester/library/src/main/scala/Macros.scala:160:25 
[error] 160 |            index => '{  case ${index} => f${index}() }.unseal //doesnt seem to work
[error]     |                         ^^^^
[error]     |                         '}' expected, but 'case' found

So I tried a last way which consisted in Invoking the CaseDef apply method:

        def continue: Option[T] = ${
          val caseDefs: List[CaseDef] = (0 until nbFunDefs).map[CaseDef] {
            index => CaseDef('{index}.unseal, None, '{f${index}()}.unseal)
            //index => '{  case ${index} => f${index}() }.unseal //doesnt seem to work
          }

          Match('{state}.unseal, caseDefs).seal.cast[Option[T]] 
        } 

This seems to work better as the compiler now complains about such an error where "f$" is not found. Here what I tried to achieve was a function call using the identifier of a function "fi" where i is the index of the function.

[error] -- [E006] Unbound Identifier Error: /home/snif/hackerspace/semester/library/src/main/scala/Macros.scala:159:54 
[error] 159 |            index => CaseDef('{index}.unseal, None, '{f${index}()}.unseal)
[error]     |                                                                                  ^^
[error]     |                                                      Not found: f$
liufengyun commented 4 years ago

Generating pattern matching, while it is doable, there is some hassle. A simpler way is to generate if/then/else, which is a little verbose, but semantically equivalent.

WiseIndian commented 4 years ago

Oh yeah.. that's right we are only testing an Int in the end. Good idea. Thanks