# Methods' composition

Consider

``````def gM(x: String, leftPar:Char, rightPar:Char): String = { "g"+leftPar+x+rightPar}
def fM(x: String): String = "f("+x+")"
``````

Why this works

``````(fM _ andThen (gM(_: String, '[', ']')))("x")
//g[f(x)]
``````

but not this

`(fM _ andThen gM(_: String, '[', ']'))("x")`

My previous attempt to explain how underscore works is here.

The underscore expands at the enclosing `Expr` which is not just a bare underscore.

``````f(_)
``````

is

``````x => f(x)
``````

as one might wish and not

``````f(x => x)
``````

Here, â€śfunction argumentâ€ť is an `Expr`.

``````f(_ + 1)
``````

is

``````f(x => x + 1)
``````

because the argument to `f` is not just an underscore, but an expression with underscore as a subexpression.

The nice feature of infix is that the operands are infix expressions and donâ€™t â€śbindâ€ť the underscore.

Here is an example, although you wouldnâ€™t normally write it this way:

``````List(42).flatMap(List(_ + 1))  // doesn't work, see previous
``````

but

``````List(42).flatMap(List apply _ + 1)  // looks weird but works
``````

In the working example, the underscore is not â€śboundâ€ť at the infix expressions, but at the enclosing expression which is the argument to `flatMap`. Normal precedence applies, so it means what the broken form looks like it should mean:

``````List(42).flatMap(x => List(x + 1))
``````

Here is the function composition in Scala 3, where itâ€™s easier to write `f` instead of `f _`:

``````scala> def g(s: String, left: Char, right: Char) = s"g\$left\$s\$right"
def g(s: String, left: Char, right: Char): String

scala> def f(s:String) = s"f(\$s)"
def f(s: String): String

scala> f andThen g(_, '[', ']')
-- [E081] Type Error: --------------------------------------------------------------------------------------------------
1 |f andThen g(_, '[', ']')
|            ^
|            Missing parameter type
|
|            I could not infer the type of the parameter _\$1 of expanded function:
|            _\$1 => f andThen g(_\$1, '[', ']').
1 error found

scala> g(_, '[', ']')
val res0: String => String = Lambda\$1450/0x00000008010c0410@6ceb11f9

scala> f andThen res0
val res1: String => String = scala.Function1\$\$Lambda\$1469/0x00000008010dc520@31b650e9

scala> res1("X")
val res2: String = g[f(X)]

scala> f.andThen(g(_, '[', ']'))
val res3: String => String = scala.Function1\$\$Lambda\$1469/0x00000008010dc520@6c06b1bc
``````

This is the converse of the previous example, where the broken form doesnâ€™t work because the infix expression which is the operand to `andThen` does not bind the underscore. Turning it into a regular argument in regular function call syntax makes it an `Expr` that does bind the underscore.

Both Scala 2 and 3 show the erroneous expansion of the broken form.

As a further nuance, `x op y` has infix expressions, but `x op (y)` turns the `y` into an `Expr` production, just the same as `x.op(y)`. `(x, y, z)` is `Expr`s in parens, where it doesnâ€™t matter whether you see them as args to a function or a tuple.

1 Like

I understand your explanation, but I wonder about the error messages (scala 2.13.8):

``````scala> (fF andThen gM(_: String, '[', ']'))("x")
^
error: type mismatch;
found   : String
required: String => ?
``````

vs.

``````scala> (fF andThen gM(_, '[', ']'))("x")
^
error: missing parameter type for expanded function ((<x\$1: error>) => fF.andThen(gM(x\$1, '[', ']')))
``````

The first one is totally confusing for me, whereas the second is quite informative and gives hints about the placeholder binding.

Would you say that in the infix notation `a op b` does not automatically translate to `(a) op (b)`?

`a op b` desugars to `a.op(b)`

We tend to say â€śdesugarâ€ť to mean â€śsyntax sweetenerâ€ť with no change in behavior.

If we assume incorrectly that `x` is syntactically the same as `(x)`, then we are confused why parens make a difference, because weâ€™re used to wrapping subexpressions in parens for clarity.

The error message is pretty challenging. Here is an example session with Scala 3.

``````scala> f andThen g(_, '[', ']')
-- [E081] Type Error: --------------------------------------------------------------------------------------------------
1 |f andThen g(_, '[', ']')
|            ^
|            Missing parameter type
|
|            I could not infer the type of the parameter _\$1 of expanded function:
|            _\$1 => f andThen g(_\$1, '[', ']').
1 error found

scala> f andThen g(_: String, '[', ']')
-- [E007] Type Mismatch Error: -----------------------------------------------------------------------------------------
1 |f andThen g(_: String, '[', ']')
|          ^^^^^^^^^^^^^^^^^^^^^^
|          Found:    String
|          Required: String => Any
|
| longer explanation available when compiling with `-explain`
1 error found

scala> :replay -explain
Unknown command: ":replay", run ":help" for a list of commands

scala> :help
The REPL has several commands available:

:help                    print this summary
:load <path>             interpret lines in a file
:quit                    exit the interpreter
:type <expression>       evaluate the type of the given expression
:doc <expression>        print the documentation for the given expression
:imports                 show import history
:reset [options]         reset the repl to its initial state, forgetting all session entries
:settings <options>      update compiler options, if possible

scala> :settings -explain

scala> f andThen g(_: String, '[', ']')
-- [E007] Type Mismatch Error: -----------------------------------------------------------------------------------------
1 |f andThen g(_: String, '[', ']')
|          ^^^^^^^^^^^^^^^^^^^^^^
|          Found:    String
|          Required: String => Any
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| Tree: g(_\$1:String, '[', ']')
| I tried to show that
|   String
| conforms to
|   String => Nothing
| but the comparison trace ended with `false`:
|
|   ==> String  <:  String => Nothing
|     ==> String  <:  String => Nothing
|     <== String  <:  String => Nothing = false
|   <== String  <:  String => Nothing = false
|
| The tests were made under a constraint with:
|  uninstantiated variables:
|  constrained types: [A](g: String => A): String => A, [A](g: String => A): String => A
|  bounds:
|      A
|      A
|  ordering:
---------------------------------------------------------------------------------------------------------------------
1 error found

scala>

scala> :settings -explain -Vprint:parser,typer
Flag -explain set repeatedly

scala> f andThen g(_: String, '[', ']')
-- [E007] Type Mismatch Error: -----------------------------------------------------------------------------------------
1 |f andThen g(_: String, '[', ']')
|          ^^^^^^^^^^^^^^^^^^^^^^
|          Found:    String
|          Required: String => Any
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| Tree: g(_\$1:String, '[', ']')
| I tried to show that
|   String
| conforms to
|   String => Nothing
| but the comparison trace ended with `false`:
|
|   ==> String  <:  String => Nothing
|     ==> String  <:  String => Nothing
|     <== String  <:  String => Nothing = false
|   <== String  <:  String => Nothing = false
|
| The tests were made under a constraint with:
|  uninstantiated variables:
|  constrained types: [A](g: String => A): String => A, [A](g: String => A): String => A
|  bounds:
|      A
|      A
|  ordering:
---------------------------------------------------------------------------------------------------------------------
[[syntax trees at end of                     typer]] // rs\$line\$6
package <empty> {
final lazy module val rs\$line\$6: rs\$line\$6 = new rs\$line\$6()
final module class rs\$line\$6() extends Object() { this: rs\$line\$6.type =>
val res3: String => String => Nothing =
{
def \$anonfun(_\$1: String): String => Nothing =
{
def \$anonfun(s: String): String = f(s)
closure(\$anonfun)
}.andThen[Nothing](g(_\$1:String, '[', ']'))
closure(\$anonfun)
}
}
}

1 error found
``````

Here is the tree in Scala 2 under `-Vprint:parser,typer`:

``````scala> (f _) andThen g(_: String, '[', ']')
[[syntax trees at end of                    parser]] // <console>
package \$line7 {
sealed class \$read extends _root_.scala.Serializable {
def <init>() = {
super.<init>();
()
};
sealed class \$iw extends _root_.java.io.Serializable {
def <init>() = {
super.<init>();
()
};
val res2 = ((x\$1: String) => (f: (() => <empty>)).andThen(g((x\$1: String), '[', ']')))
};
val \$iw = new \$iw()
};
def <init>() = {
super.<init>();
()
};
}
}

^
error: type mismatch;
found   : String
required: String => ?
[[syntax trees at end of                     typer]] // <console>
package \$line7 {
sealed class \$read extends AnyRef with Serializable {
()
};
sealed class \$iw extends AnyRef with java.io.Serializable {
def <init>(): \$iw = {
\$iw.super.<init>();
()
};
private[this] val <res2: error>: String => <error> = ((x\$1: String) => ((s: String) => \$read.this.\$line4\$read.\$iw.f(s)).andThen[A](g((x\$1: String), '[', ']')));
<stable> <accessor> def <res2: error>: String => <error> = \$iw.this.<res2: error>
};
private[this] val \$iw: \$iw = new \$read.this.\$iw();
<stable> <accessor> def \$iw: \$iw = \$read.this.\$iw
};
object \$read extends scala.AnyRef with java.io.Serializable {
def <init>(): type = {
()
};
<synthetic> private def writeReplace(): Object = new scala.runtime.ModuleSerializationProxy(classOf[\$line7.\$read\$])
}
}
``````

Itâ€™s too bad the parser output comes before the error message and caret. Scala 3 underlines the whole expression, which is nice, while Scala 2 just puts the caret at the left paren of the application of `g`.

``````val res2 = ((x\$1: String) => (f: (() => <empty>)).andThen(g((x\$1: String), '[', ']')))
``````

In this form, and maybe also in Scala 3â€™s closure notation, you can see that the `String` value it found is the result of `g`. Although Scala 3 underlines it, I agree that itâ€™s confusing. It doesnâ€™t tell us where a `String` value came from, and it doesnâ€™t tell us what requires a `String => Any` or `String => ?`.

It could also detect that a function expansion is involved and show us the expansion. For â€śMissing parameter typeâ€ť, it shows us the expansion in an addendum.

It could also notice that the expansion does not align with where a function type is expected but not found.

If it told us that much, `-explain` would really pull its weight.

1 Like

Thank you @som-snytt . I will study the debugging information, some messages reveal unexpected things