MainAnnotation
MainAnnotation
⚠️ 이 기능은 https://github.com/scala/scala3/pull/19937 에서 제거되었습니다. 매크로 어노테이션(macro annotations)으로 대체되었어요. SIP-63(https://github.com/scala/improvement-proposals/pull/80)을 참고하세요.
MainAnnotation은 @main 같은 main 어노테이션을 정의할 수 있는 일반적인 방법을 제공해요.
본문
이 기능은 https://github.com/scala/scala3/pull/19937 에서 제거됐어요. 매크로 어노테이션으로 대체되었죠. SIP-63 https://github.com/scala/improvement-proposals/pull/80 을 보세요.
MainAnnotation은 @main 같은 main 어노테이션을 정의하는 일반적인 방법을 제공해요.
사용자가 MainAnnotation을 확장하는 어노테이션으로 메서드에 주석을 달면, main 메서드를 가진 클래스가 생성돼요. 그 main 메서드는 커맨드라인 인자를 파싱하고 애플리케이션을 실행하는 데 필요한 코드를 담게 됩니다.
/** Sum all the numbers
*
* @param first Fist number to sum
* @param rest The rest of the numbers to sum
*/
@myMain def sum(first: Int, second: Int = 0, rest: Int*): Int = first + second + rest.sum
object foo {
def main(args: Array[String]): Unit = {
val mainAnnot = new myMain()
val info = new Info(
name = "foo.main",
documentation = "Sum all the numbers",
parameters = Seq(
new Parameter("first", "scala.Int", hasDefault=false, isVarargs=false, "Fist number to sum", Seq()),
new Parameter("second", "scala.Int", hasDefault=true, isVarargs=false, "", Seq()),
new Parameter("rest", "scala.Int" , hasDefault=false, isVarargs=true, "The rest of the numbers to sum", Seq())
)
)
val mainArgsOpt = mainAnnot.command(info, args)
if mainArgsOpt.isDefined then
val mainArgs = mainArgsOpt.get
val args0 = mainAnnot.argGetter[Int](info.parameters(0), mainArgs(0), None) // using a parser of Int
val args1 = mainAnnot.argGetter[Int](info.parameters(1), mainArgs(1), Some(() => sum$default$1())) // using a parser of Int
val args2 = mainAnnot.varargGetter[Int](info.parameters(2), mainArgs.drop(2)) // using a parser of Int
mainAnnot.run(() => sum(args0(), args1(), args2()*))
}
}
main 메서드의 구현은 먼저 어노테이션을 인스턴스화하고 나서 command를 호출해요. command를 호출할 때 인자를 확인하고 전처리할 수 있어요. 그 다음에는 일련의 인자 getter를 정의하는데, 각 파라미터에 대해 argGetter를 호출하고 마지막 파라미터가 가변 인자(varargs)면 varargGetter를 호출해요. argGetter는 기본 인자를 계산하는 선택적 람다를 받아요. 마지막으로 run 메서드를 호출해서 애플리케이션을 실행해요. run은 어노테이션이 붙은 메서드를 인스턴스화된 인자들로 호출하는 내용(이름-전달 by-name 인자)을 담고 있고, 인자는 argGetter/varargGetter의 람다들을 씁니다.
모든 인자를 위치적으로 받는 myMain 구현 예시가 있어요. util.CommandLineParser.FromString을 사용하고 기본 인자가 없다고 기대해요. 단순하게 하기 위해 전처리나 파싱 중 어떤 오류든 크래시로 이어집니다.
// Parser used to parse command line arguments
import scala.util.CommandLineParser.FromString[T]
// Result type of the annotated method is Int and arguments are parsed using FromString
@experimental class myMain extends MainAnnotation[FromString, Int]:
import MainAnnotation.{ Info, Parameter }
def command(info: Info, args: Seq[String]): Option[Seq[String]] =
if args.contains("--help") then
println(info.documentation)
None // do not parse or run the program
else if info.parameters.exists(_.hasDefault) then
println("Default arguments are not supported")
None
else if info.hasVarargs then
val numPlainArgs = info.parameters.length - 1
if numPlainArgs > args.length then
println("Not enough arguments")
None
else
Some(args)
else
if info.parameters.length > args.length then
println("Not enough arguments")
None
else if info.parameters.length < args.length then
println("Too many arguments")
None
else
Some(args)
def argGetter[T](param: Parameter, arg: String, defaultArgument: Option[() => T])(using parser: FromString[T]): () => T =
() => parser.fromString(arg)
def varargGetter[T](param: Parameter, args: Seq[String])(using parser: FromString[T]): () => Seq[T] =
() => args.map(arg => parser.fromString(arg))
def run(program: () => Int): Unit =
println("executing program")
val result = program()
println("result: " + result)
println("executed program")
end myMain