Capítulo 1. Tareas desde la línea de comandos
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
Lo más probable es que uno de los primeros pasos en tu viaje con Scala 3 implique trabajar en la línea de comandos. Por ejemplo, después de instalar Scala como se muestra en "Instalación de Scala", es posible que quieras iniciar el REPL (Read/Eval/Print/Loop de Scala) escribiendo scala
en la línea de comandos de tu sistema operativo. O puede que quieras crear un pequeño proyecto "Hola, mundo" de un solo archivo y luego compilarlo y ejecutarlo. Dado que muchas personas empezarán a trabajar con Scala en estas tareas de la línea de comandos, aquí se tratan en primer lugar.
El REPL es un intérprete de comandos. Es una zona de juegos donde puedes hacer pequeñas pruebas para ver cómo funcionan Scala y sus bibliotecas de terceros. Si estás familiarizado con el JShell de Java, el irb
de Ruby, el shell de Python o IPython, o el ghci
de Haskell, el REPL de Scala es similar a todos ellos. Como se muestra en la Figura 1-1, sólo tienes que iniciar el REPL escribiendo scala
en la línea de comandos de tu sistema operativo, después escribe tus expresiones Scala y se evaluarán en el intérprete de comandos.
Siempre que quieras probar algún código Scala, la REPL es un entorno de juego estupendo. No es necesario crear un proyecto completo: basta con que pongas tu código de prueba en la REPL y experimentes con él hasta que sepas que funciona. Dado que la REPL es una herramienta tan importante, sus funciones más importantes se muestran en las dos primeras recetas de este capítulo.
Aunque el REPL es estupendo, no es el único juego de la ciudad. El REPL de Ammonite se creó originalmente para Scala 2, y tenía muchas más funciones que el REPL de Scala 2, entre ellas:
-
La posibilidad de importar código desde repositorios GitHub y Maven
-
La posibilidad de guardar y restaurar sesiones
-
Impresión bonita
-
Edición multilínea
En el momento de escribir esto, Ammonite todavía se está portando a Scala 3, pero muchas funciones importantes ya funcionan. Consulta la Receta 1.3 para ver ejemplos de cómo utilizar esas funciones.
Por último, cuando necesites compilar proyectos Scala, normalmente utilizarás una herramienta de compilación como sbt, que se muestra en el capítulo 17. Pero si alguna vez quieres compilar y ejecutar una pequeña aplicación Scala, como una que sólo tenga uno o dos archivos, puedes compilar tu código con el comando scalac
y ejecutarlo con scala
, igual que haces en Java con los comandos javac
y java
. Este proceso se demuestra en la Receta 1.4, y después, la Receta 1.6 muestra cómo puedes ejecutar aplicaciones que empaquetas como un archivo JAR con los comandos java
o scala
.
1.1 Primeros pasos con el REPL de Scala
Solución
Si has utilizado entornos REPL en lenguajes como Java, Python, Ruby y Haskell, el REPL de Scala te resultará muy familiar. Para iniciar la REPL, escribe scala
en la línea de comandos de tu sistema operativo. Cuando se inicie el REPL, es posible que veas un mensaje inicial, seguido de la indicación scala>
:
$ scala Welcome to Scala 3.0 Type in expressions for evaluation. Or try :help. scala> _
El prompt indica que ahora estás utilizando el REPL de Scala. Dentro del entorno REPL puedes probar todo tipo de experimentos y expresiones diferentes:
scala> val x = 1 x: Int = 1 scala> val y = 2 y: Int = 2 scala> x + y res0: Int = 3 scala> val x = List(1, 2, 3) x: List[Int] = List(1, 2, 3) scala> x.sum res1: Int = 6
Como se muestra en estos ejemplos:
-
Después de introducir tu comando, la salida de REPL muestra el resultado de tu expresión, incluida la información sobre el tipo de datos.
-
Si no asignas un nombre de variable, como en el tercer ejemplo, el REPL crea su propia variable, empezando por
res0
, luegores1
, etc. Puedes utilizar estos nombres de variable como si los hubieras creado tú mismo:
scala> res1.getClass res2: Class[Int] = int scala> res1 + 2 res3: Int = 8
Tanto los desarrolladores principiantes como los experimentados escriben código en el REPL todos los días para ver rápidamente cómo funcionan las funciones de Scala y sus propios algoritmos.
Completar pestaña
Hay algunos trucos sencillos que pueden hacer más eficaz el uso del REPL. Uno de ellos es utilizar el tabulador para ver los métodos disponibles en un objeto. Para ver cómo funciona el completado con tabulador, escribe el número 1
, luego un decimal y, a continuación, pulsa la tecla Tabulador. El REPL responde mostrando las decenas de métodos disponibles en una instancia de Int
:
scala> 1. != finalize round ## floatValue self % floor shortValue & formatted sign * getClass signum many more here ...
También puedes limitar la lista de métodos que se muestran escribiendo la primera parte del nombre de un método y pulsando después la tecla Tabulador. Por ejemplo, si quieres ver todos los métodos disponibles en un List
, escribe List(1).
seguido de la tecla Tabulador, y verás más de doscientos métodos. Pero si sólo te interesan los métodos de un List
que empiecen por los caracteres to
, escribe List(1).to
y luego pulsa Tabulador, y la salida se reducirá a estos métodos:
scala> List(1).to to toIndexedSeq toList toSet toTraversable toArray toIterable toMap toStream toVector toBuffer toIterator toSeq toString
Debate
Utilizo la REPL para crear muchos pequeños experimentos, y también me ayuda a entender algunas conversiones de tipo que Scala realiza automáticamente. Por ejemplo, cuando empecé a trabajar con Scala y escribí el siguiente código en la REPL, no sabía de qué tipo era la variable x
:
scala> val x = (3, "Three", 3.0) val x: (Int, String, Double) = (3,Three,3.0)
Con el REPL, es fácil ejecutar pruebas como ésta y luego llamar a getClass
sobre una variable para ver su tipo:
scala> x.getClass val res0: Class[? <: (Int, String, Double)] = class scala.Tuple3
Aunque parte de esa línea de resultados es difícil de leer cuando empiezas a trabajar con Scala, el texto de la parte derecha de =
te permite saber que el tipo es una clase Tuple3
.
También puedes utilizar el comando :type
del REPL para ver información similar, aunque actualmente no muestra el nombre Tuple3
:
scala> :type x (Int, String, Double)
Sin embargo, suele ser útil en muchos otros casos:
scala> :type 1 + 1.1 Double scala> :type List(1,2,3).map(_ * 2.5) List[Double]
Aunque se trata de ejemplos sencillos, verás que el REPL es extremadamente útil cuando trabajes con código más complicado y bibliotecas con las que no estés familiarizado.
Iniciar la REPL dentro de sbt
También puedes iniciar una sesión REPL de Scala desde dentro del intérprete de comandos sbt. Como se muestra en la Receta 17.5, "Entender otros comandos de sbt", sólo tienes que iniciar el intérprete de comandos sbt dentro de un proyecto sbt:
$ sbt MyProject> _
A partir de ahí, utiliza el comando console
o consoleQuick
:
MyProject> console scala> _
El comando console
compila los archivos de código fuente del proyecto, los coloca en el classpath e inicia la REPL. El comando consoleQuick
inicia la REPL con las dependencias del proyecto en el classpath, pero sin compilar los archivos de código fuente del proyecto. Esta segunda opción es útil para cuando tu código no se compila, o cuando quieres probar algún código de prueba con tus dependencias (bibliotecas).
Ver también
Si te gusta la idea de un entorno REPL pero quieres probar alternativas al REPL por defecto, hay varias alternativas gratuitas estupendas:
-
El REPL Ammonite tiene más funciones que el REPL, y se demuestra en la Receta 1.3.
-
Scastie es una alternativa web al REPL que admite opciones sbt y te permite añadir bibliotecas externas a tu entorno.
-
ScalaFiddle también es una alternativa basada en la web.
-
Tanto el IDE IntelliJ IDEA como el IDE Visual Studio Code (VS Code) tienen hojas de trabajo, que son similares a la REPL.
1.2 Cargar código fuente y archivos JAR en el REPL
Solución
Utiliza el comando :load
para leer archivos de código fuente en el entorno REPL. Por ejemplo, dado este código en un archivo llamado Person.scala, en un subdirectorio llamadomodelos:
class
Person
(
val
name
:
String
):
override
def
toString
=
name
puedes cargar ese código fuente en el entorno REPL en ejecución de la siguiente manera:
scala> :load models/Person.scala // defined class Person
Una vez cargado el código en el REPL, puedes crear una nueva instancia de Person
:
scala> val p = Person("Kenny") val p: Person = Kenny
Ten en cuenta, sin embargo, que si tu código fuente tiene una declaración package
:
// Dog.scala file
package
animals
class
Dog
(
val
name
:
String
)
el comando :load
fallará:
scala> :load Dog.scala 1 |package foo |^^^ |Illegal start of statement
Los archivos de código fuente no pueden utilizar paquetes en la REPL, por lo que en situaciones como ésta tendrás que compilarlos en un archivo JAR y luego incluirlos en el classpath cuando inicies la REPL. Por ejemplo, así es como utilizo la versión 0.2.0 de mi biblioteca Simple Test con la REPL de Scala 3:
// start the repl like this $ scala -cp simpletest_3.0.0-0.2.0.jar scala> import com.alvinalexander.simpletest.SimpleTest.* scala> isTrue(1 == 1) true
En el momento de escribir esto, no puedes añadir un JAR a una sesión REPL que ya se esté ejecutando, pero es posible que esa función se añada en el futuro.
Debate
Otra cosa que conviene saber es que los archivos de clase compilados en el directorio actual se cargan automáticamente en el REPL. Por ejemplo, si pones este código en un archivo llamado Cat.scala y luego lo compilas con scalac
, se creará un archivo Cat.class:
case
class
Cat
(
name
:
String
)
Si inicias el REPL en el mismo directorio que ese archivo de clase, puedes crear un nuevo Cat
:
scala> Cat("Morris") val res0: Cat = Cat(Morris)
En los sistemas Unix puedes utilizar esta técnica para personalizar tu entorno REPL. Para ello, sigue estos pasos:
-
Crea un subdirectorio en tu directorio personal llamado repl. En mi caso, creo este directorio como /Usuarios/al/repl. (Utiliza el nombre que prefieras para este directorio).
-
Pon los archivos *.class que quieras en ese directorio.
-
Crea un alias o un script de shell que puedas utilizar para iniciar el REPL en ese directorio.
En mi sistema puse un archivo llamado Repl.scala en mi directorio ~/repl, con estoscontenidos:
import
sys
.
process
.
*
def
clear
=
"clear"
.
!
def
cmd
(
cmd
:
String
)
=
cmd
.
!!
def
ls
(
dir
:
String
)
=
println
(
cmd
(
s"ls -al
$
dir
"
))
def
help
=
println
(
"\n=== MY CONFIG ==="
)
"cat /Users/Al/repl/Repl.scala"
.
!
case
class
Person
(
name
:
String
)
val
nums
=
List
(
1
,
2
,
3
)
Luego compilo ese código con scalac
para crear sus archivos de clase en ese directorio. Luego creo y utilizo este alias para iniciar el REPL:
alias
repl
=
"cd ~/repl; scala; cd -"
Ese alias me mueve al directorio ~/repl, inicia la REPL y me devuelve a mi directorio actual cuando salgo de la REPL.
Como otro enfoque, puedes crear un script de shell llamado repl
, hacerlo ejecutable y colocarlo en tu directorio ~/bin (o en cualquier otro lugar de tu PATH
):
#!/bin/sh
cd
~/
repl
scala
Como un script de shell se ejecuta en un subproceso, volverás a tu directorio original cuando salgas del REPL.
Utilizando este enfoque, tus métodos personalizados se cargarán en el REPL cuando se inicie, de modo que puedas utilizarlos dentro del intérprete de comandos scala
:
clear
// clear the screen
cmd
(
"ps"
)
// run the 'ps' command
ls
(
"."
)
// run 'ls' in the current directory
help
// displays my Repl.scala file as a form of help
Utiliza esta técnica para precargar cualquier otra definición personalizada que quieras utilizar en el REPL.
1.3 Primeros pasos con el REPL de Ammonite
Solución
El REPL de Ammonite funciona igual que el REPL de Scala: sólo tienes que descargarlo e instalarlo, y luego iniciarlo con su comando amm
. Al igual que el REPL de Scala por defecto, evalúa expresiones Scala y asigna un nombre de variable si no le proporcionas uno:
@ val x = 1 + 1 x: Int = 2 @ 2 + 2 res0: Int = 4
Pero Ammonite tiene muchas funciones adicionales. Puedes cambiar el símbolo del sistema con este comando:
@ repl.prompt() = "yo: " yo: _
A continuación, si tienes estas expresiones Scala en un archivo llamado Repl.scala, en un subdirectorio llamado foo:
import
sys
.
process
.
*
def
clear
=
"clear"
.
!
def
cmd
(
cmd
:
String
)
=
cmd
.
!!
def
ls
(
dir
:
String
)
=
println
(
cmd
(
s"ls -al
$
dir
"
))
puedes importarlos a tu Ammonite REPL con este comando:
@ import $file.foo.Repl, Repl.*
Entonces podrás utilizar esos métodos dentro de Ammonite:
clear
// clear the screen
cmd
(
"ps"
)
// run the 'ps' command
ls
(
"/tmp"
)
// use 'ls' to list files in /tmp
Del mismo modo, puedes importar un archivo JAR llamado simpletest_3.0.0-0.2.0.jar en un subdirectorio llamado foo a tu sesión REPL de amm
utilizando la variable $cp
de Ammonite:
// import the jar file
import
$cp
.
foo
.
`simpletest_3.0.0-0.2.0.jar`
// use the library you imported
import
com
.
alvinalexander
.
simpletest
.
SimpleTest
.
*
isTrue
(
1
==
1
)
El comando import ivy
te permite importar dependencias de Maven Central (y otros repositorios) y utilizarlas en tu shell actual:
yo: import $ivy.`org.jsoup:jsoup:1.13.1` import $ivy.$ yo: import org.jsoup.Jsoup, org.jsoup.nodes.{Document, Element} import org.jsoup.Jsoup yo: val html = "<p>Hi!</p>" html: String = "<p>Hi!</p>" yo: val doc: Document = Jsoup.parse(html) doc: Document = <html> ... yo: doc.body.text res2: String = "Hi!"
El comando time
integrado en Ammonite te permite cronometrar el tiempo que tarda en ejecutarse tu código:
@ time(Thread.sleep(1_000)) res2: (Unit, FiniteDuration) = ((), 1003788992 nanoseconds)
La capacidad de autocompletar de Ammonite es impresionante. Sólo tienes que escribir una expresión como ésta y pulsar Tab después del decimal:
@ Seq("a").map(x => x.
Al hacerlo, Ammonite muestra una larga lista de métodos disponibles en x
-que es un String
-empezando por estos métodos:
def intern(): String def charAt(x$0: Int): Char def concat(x$0: String): String much more output here ...
Esto está bien porque te muestra no sólo los nombres de los métodos, sino también sus parámetros de entrada y su tipo de retorno.
Debate
La lista de funciones de Ammonite es larga. Otra muy buena es que puedes utilizar un archivo de configuración de inicio, igual que utilizas un archivo de inicio .bashrc o .bash_profile de Unix. Sólo tienes que poner algunas expresiones en un archivo ~/.ammonite/predef.sc:
import
sys
.
process
.
*
repl
.
prompt
()
=
"yo: "
def
clear
=
"clear"
.
!
def
cmd
(
cmd
:
String
)
=
cmd
.
!!
def
ls
(
dir
:
String
)
=
println
(
cmd
(
s"ls -al
$
dir
"
))
def
reset
=
repl
.
sess
.
load
()
// similar to the scala repl ':reset' command
Entonces, cuando inicies el REPL de Ammonite, tu prompt cambiará a yo:
, y esos otros métodos estarán disponibles para ti.
Otra gran característica es que puedes guardar una sesión REPL, y guardará todo lo que hayas hecho hasta ese momento. Para probarlo, crea una variable en el REPL y luego guarda la sesión:
val
remember
=
42
repl
.
sess
.
save
()
A continuación, crea otra variable:
val
forget
=
0
Ahora vuelve a cargar la sesión, y verás que la variable remember
sigue disponible, pero la variable forget
se ha olvidado, como deseabas:
@ repl.sess.load() res3: SessionChanged = SessionChanged(removedImports = Set('forget), addedImports = Set(), removedJars = Set(), addedJars = Set()) @ remember res4: Int = 42 @ forget |val res5 = forget | ^^ | Not found: forget
También puedes guardar y cargar varias sesiones dándoles nombres diferentes, así:
// do some work
val
x
=
1
repl
.
sess
.
save
(
"step 1"
)
// do some more work
val
y
=
2
repl
.
sess
.
save
(
"step 2"
)
// reload the first session
repl
.
sess
.
load
(
"step 1"
)
x
// this will be found
y
// this will not be found
Consulta la documentación de Ammonite para conocer másfunciones.
1.4 Compilar con scalac y ejecutar con scala
Problema
Aunque normalmente utilizarás una herramienta de compilación como sbt o Mill para compilar aplicaciones Scala, en ocasiones puede que quieras utilizar herramientas más básicas para compilar y ejecutar pequeños programas de prueba, del mismo modo que podrías utilizar javac
y java
con pequeñas aplicaciones Java.
Solución
Compila pequeños programas con scalac
, y ejecútalos con scala
. Por ejemplo, dado este archivo de código fuente Scala llamado Hello.scala:
@main
def
hello
=
println
(
"Hello, world"
)
compílalo en la línea de comandos con scalac
:
$ scalac Hello.scala
A continuación, ejecútalo con scala
, dando al comando scala
el nombre del método @main
que has creado:
$ scala hello Hello, world
Debate
Compilar y ejecutar clases es igual que en Java, incluidos conceptos como el classpath. Por ejemplo, imagina que tienes una clase llamada Pizza
en un archivo llamado Pizza.scala, y que depende de un tipo Topping
:
class
Pizza
(
val
toppings
:
Topping
*
):
override
def
toString
=
toppings
.
toString
Suponiendo que Topping
se defina así:
enum
Topping
:
case
Cheese
,
Mushrooms
y que está en un archivo llamado Topping .scala, y que se ha compilado en Topping.class en un subdirectorio llamado clases, compila Pizza.scala de la siguiente manera:
$ scalac -classpath classes Pizza.scala
Ten en cuenta que el comando scalac
tiene muchas opciones adicionales que puedes utilizar. Por ejemplo, si añades la opción -verbose
al comando anterior, verás cientos de líneas de salida adicionales que muestran cómo está funcionando scalac
. Estas opciones pueden cambiar con el tiempo, así que utiliza la opción -help
para ver información adicional:
$ scalac -help Usage: scalac <options> <source files> where possible standard options include: -P Pass an option to a plugin, e.g. -P:<plugin>:<opt> -X Print a synopsis of advanced options. -Y Print a synopsis of private options. -bootclasspath Override location of bootstrap class files. -classpath Specify where to find user class files. much more output here ...
Métodos principales
Mientras hablamos de compilar métodos main
, es útil saber que se pueden declarar de dos formas con Scala 3:
-
Utilizar la anotación
@main
en un método -
Declarar un método
main
con la firma adecuada en un métodoobject
Como se muestra en la Solución, un simple método @main
que no toma parámetros de entrada puede declararse así:
@main
def
hello
=
println
(
"Hello, world"
)
También puedes declarar un método @main
para que tome los parámetros que quieras en la línea de comandos, como tomar un String
y un Int
en este ejemplo:
@main
def
hello
(
name
:
String
,
age
:
Int
):
Unit
=
println
(
s"Hello,
$
name
, I think you are
$
age
years old."
)
Después de compilar ese código con scalac
, se puede ejecutar así:
$ scala hello "Lori" 44 Hello, Lori, I think you are 44 years old.
Para el segundo enfoque, declarar un método main
dentro de un object
es igual que declarar un método main en Java, y la firma del método main
de Scala debe tener este aspecto:
object
YourObjectName
:
// the method must take `Array[String]` and return `Unit`
def
main
(
args
:
Array
[
String
]):
Unit
=
// your code here
Si estás familiarizado con Java, ese código Scala es análogo a este código Java:
public
class
YourObjectName
{
public
static
void
main
(
String
[]
args
)
{
// your code here
}
}
1.5 Desensamblar y descompilar código Scala
Solución
La principal forma de desensamblar código Scala es con el comando javap
. También puedes utilizar un descompilador para convertir tus archivos de clase de nuevo en código fuente Java, y esta opción se muestra en la Discusión.
Utilizar javap
Como tus archivos de código fuente Scala se compilan en archivos de clase JVM normales, puedes utilizar el comando javap
para desensamblarlos. Por ejemplo, supongamos que has creado un archivo llamado Person.scala
que contiene este código fuente:
class
Person
(
var
name
:
String
,
var
age
:
Int
)
A continuación, compila ese archivo con scalac
:
$ scalac Person.scala
Ahora puedes desensamblar el archivo Person.class resultante en su firma utilizando javap
, así:
$ javap -public Person Compiled from "Person.scala" public class Person { public Person(java.lang.String, int); public java.lang.String name(); public void name_$eq(java.lang.String); public int age(); public void age_$eq(int); }
Esto muestra la firma pública de la clase Person
, que es su API pública, o interfaz. Incluso en un ejemplo sencillo como éste puedes ver cómo el compilador de Scala hace su trabajo por ti, creando métodos como name()
, name_$eq
, age()
, y age_$eq
. La Discusión muestra ejemplos más detallados.
Si quieres, puedes ver información adicional con la opción javap -private
:
$ javap -private Person Compiled from "Person.scala" public class Person { private java.lang.String name; // new private int age; // new public Person(java.lang.String, int); public java.lang.String name(); public void name_$eq(java.lang.String); public int age(); public void age_$eq(int); }
El javap
tiene varias opciones más que son útiles. Utiliza la opción -c
para ver los comandos reales que componen el código de bytes de Java, y añádele la opción -verbose
para ver muchos más detalles. Ejecuta javap -help
para ver los detalles de todas las opciones.
Debate
Desensamblar archivos de clase con javap
puede ser una forma útil de entender cómo funciona Scala. Como has visto en el primer ejemplo con la clase Person
, definir los parámetros del constructor name
y age
como campos var
te genera bastantes métodos.
Como segundo ejemplo, quita el atributo var
a esos dos campos, de modo que tengas esta definición de clase:
class
Person
(
name
:
String
,
age
:
Int
)
Compila esta clase con scalac
, y luego ejecuta javap
en el archivo de clase resultante. Verás que el resultado es una firma de clase mucho más corta:
$ javap -public Person Compiled from "Person.scala" public class Person { public Person(java.lang.String, int); }
Por el contrario, dejar var
en ambos campos y convertir la clase en una clase case amplía significativamente la cantidad de código que Scala genera en tu nombre. Para ver esto, cambia el código en Person.scala para que tengas esta clase case:
case
class
Person
(
var
name
:
String
,
var
age
:
Int
)
Cuando compilas este código, crea dos archivos de salida, Persona.class y Person$.class. Desensambla esos dos archivos utilizando javap
:
$ javap -public Person Compiled from "Person.scala" public class Person implements scala.Product,java.io.Serializable { public static Person apply(java.lang.String, int); public static Person fromProduct(scala.Product); public static Person unapply(Person); public Person(java.lang.String, int); public scala.collection.Iterator productIterator(); public scala.collection.Iterator productElementNames(); public int hashCode(); public boolean equals(java.lang.Object); public java.lang.String toString(); public boolean canEqual(java.lang.Object); public int productArity(); public java.lang.String productPrefix(); public java.lang.Object productElement(int); public java.lang.String productElementName(int); public java.lang.String name(); public void name_$eq(java.lang.String); public int age(); public void age_$eq(int); public Person copy(java.lang.String, int); public java.lang.String copy$default$1(); public int copy$default$2(); public java.lang.String _1(); public int _2(); } $ javap -public Person$ Compiled from "Person.scala" public final class Person$ implements scala.deriving.Mirror$Product, java.io.Serializable { public static final Person$ MODULE$; public static {}; public Person apply(java.lang.String, int); public Person unapply(Person); public java.lang.String toString(); public Person fromProduct(scala.Product); public java.lang.Object fromProduct(scala.Product); }
Como se muestra, cuando defines una clase como clase case, Scala genera mucho código para ti, y esta salida muestra la firma pública de ese código. Consulta la Receta 5.14, "Generar código boilerplate con clases case", para una discusión detallada de este código.
Acerca de esos archivos .tasty
Te habrás dado cuenta de que, además de archivos .class, Scala 3 también genera archivos .tasty durante el proceso de compilación. Estos archivos se generan en lo que se conoce como formato TASTy, donde las siglas TASTy provienen del término árboles sintácticos abstractos tipados.
En cuanto a lo que son estos archivos, la documentación de la Inspección TASTy afirma: "Los archivos TASTy contienen el árbol tipado completo de una clase, incluidas las posiciones fuente y la documentación. Esto es ideal para herramientas que analizan o extraen información semántica del código".
Uno de sus usos es para la integración entre Scala 3 y Scala 2.13+. Como se indica en esta página de compatibilidad futura de Scala, "Scala 2.13 puede leer estos archivos (TASTy) para saber, por ejemplo, qué términos, tipos e implícitos están definidos en una determinada dependencia, y qué código hay que generar para utilizarlos correctamente. La parte del compilador que gestiona esto se conoce como Tasty Reader".
Ver también
-
En mi entrada del blog "Cómo crear métodos en línea en Scala 3", muestro cómo utilizar esta técnica para entender los métodos de
inline
. -
También puedes utilizar descompiladores para convertir archivos .class en código Java. Yo utilizo de vez en cuando una herramienta llamada JAD, que dejó de fabricarse en 2001, pero que sorprendentemente sigue siendo capaz de descompilar al menos parcialmente archivos de clase veinte años después. También se mencionó un descompilador mucho más moderno llamado CFR en el canal Gitter de Scala.
Para más información sobre TASTy y los archivos .tasty, consulta estos recursos:
1.6 Ejecutar archivos JAR con Scala y Java
Solución
Primero, crea un proyecto sbt básico, como se muestra en la Receta 17.1, "Crear una estructura de directorios de proyecto para sbt". A continuación, añade sbt-assembly a la configuración del proyecto añadiendo esta línea al archivo project/plugins.sbt:
// note: this version number changes several times a year
addSbtPlugin
(
"com.eed3si9n"
%
"sbt-assembly"
%
"0.15.0"
)
A continuación, coloca este archivo de código fuente Hello.scala en el directorio raíz de ese proyecto:
@main
def
hello
=
println
(
"Hello, world"
)
A continuación, crea un archivo JAR con el comando assembly
o show assembly
en el intérprete de comandos sbt:
// option 1 sbt:RunJarFile> assembly // option 2: shows the output file location sbt:RunJarFile> show assembly [info] target/scala-3.0.0/RunJarFile-assembly-0.1.0.jar
Como se muestra, la salida del comando show assembly
imprime la ubicación donde se escribe el archivo JAR de salida. Este archivo comienza con el nombre RunJarFile porque ése es el valor del campo name
de mi archivo build.sbt. Del mismo modo, la parte 0.1.0 del nombre del archivo procede del campo version
de ese archivo:
lazy
val
root
=
project
.
in
(
file
(
"."
))
.
settings
(
name
:=
"RunJarFile"
,
version
:=
"0.1.0"
,
scalaVersion
:=
"3.0.0"
)
A continuación, crea un subdirectorio Ejemplo, desplázate a ese directorio y copia el archivo JAR en ese directorio:
$ mkdir Example $ cd Example $ cp ../target/scala-3.0.0/RunJarFile-assembly-0.1.0.jar .
Como el complemento sbt-assembly empaqueta todo lo que necesitas en el archivo JAR, puedes ejecutar el método main de hello
con este comando scala
:
$ scala -cp "RunJarFile-assembly-0.1.0.jar" hello Hello, world
Ten en cuenta que si tu archivo JAR contiene varios métodos @main
en paquetes, puedes ejecutarlos con comandos similares, especificando la ruta completa a los métodos al final del comando:
scala
-
cp
"RunJarFile-assembly-0.1.0.jar"
com
.
alvinalexander
.
foo
.
mainMethod1
scala
-
cp
"RunJarFile-assembly-0.1.0.jar"
com
.
alvinalexander
.
bar
.
mainMethod2
Debate
Si (a) intentas ejecutar tu archivo JAR con el comando java
, o (b) creas el archivo JAR con sbt package
en lugar de sbt assembly
, tendrás que añadir manualmente las dependencias de tu archivo JAR a tu classpath. Por ejemplo, cuando ejecutes un archivo JAR con el comando java
, tendrás que utilizar un comando como este
$ java -cp "~/bin/scala3/lib/scala-library.jar:my-packaged-jar-file.jar" ↵ foo.bar.Hello Hello, world
Ten en cuenta que todo el comando java
debe estar en una línea, incluida la parte foo.bar.Hello
al final de la línea.
Para este enfoque necesitas encontrar el archivo scala-library.jar. En mi caso, como administro la distribución de Scala 3 manualmente, lo encontré en el directorio que se muestra. Si utilizas una herramienta como Coursier para gestionar tu instalación de Scala, los archivos que descarga se encuentran en estos directorios:
-
En macOS: ~/Library/Caches/Coursier/v1
-
En Linux: ~/.cache/coursier/v1
-
En Windows: %LOCALAPPDATA%\Coursier\Cache\v1, que, para un usuario llamado Alvin, suele corresponder a C:\Usuarios\Alvin\AppData\Local\Coursier\Cache\v1
Consulta la página Coursier Cache para obtener información actualizada sobre la ubicación de estos directorios.
¿Por qué utilizar sbt-assembly?
Ten en cuenta que si tu aplicación utiliza dependencias gestionadas o no gestionadas y utilizas sbt package
en lugar de sbt assembly
, tendrás que entender todas esas dependencias y sus dependencias transitivas, encontrar esos archivos JAR y luego incluirlos en la configuración del classpath. Por ello, se recomienda encarecidamente el uso de sbt assembly
o de una herramienta similar.
Ver también
-
Consulta la Receta 17.11, "Implementación de un único archivo JAR ejecutable", para obtener más detalles sobre cómo configurar y utilizar sbt-assembly.
Get Scala Cookbook, 2ª Edición now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.