Archivo

Archive for the ‘Fundamentos’ Category

El ADN de Scala

noviembre 25, 2011 Deja un comentario

Scala es un lenguaje de nueva generación y como tal puede aprovechar el conocimiento derivado de los errores y aciertos de lenguajes de programación anteriores. Scala incluye mecanismos de otros lenguajes que considera aciertos y evita incluir aquellos elementos que resultaron ser una mala elección. La lista de lenguajes y características correspondientes incluidas en Scala forman su particular ADN[1]. Veamos algunos elementos principales

  • La sintaxis «curly brace» de C,C++, Java
  • El modelo de objetos uniforme de Smalltalk
  • Los for-comprehension y views (inspirados en los type-classes) de Haskell
  • Los tipos abstractos inspirados en mecanismos similares de ML y OCaml
  • El tratamiento de propiedades (atributos con getters y setters) de Sather
  • El trasplante de características[1] de lenguajes funcionales a la JVM de Pizza
  • El estilo hibrido funcional/orientado objetos tambien visto en OCaml y Moby
[1] http://www.scala-lang.org/docu/files/ScalaOverview.pdf
[2] Funciónes de orden superior, programación genérica y pattern matching
Categorías: Fundamentos

Funciones y metodos

diciembre 14, 2010 Deja un comentario

En la mayoria de los casos podemos tratar funciones y metodos indistintamente. Sin embargo no son exactamente iguales, aunque los dos sirven para definir bloques de computacion. La diferencia sutil esta en que un metodo es siempre un miembro de una clase (junto con campos y tipos), mientras que una funcion no esta ligada a una clase, y como hemos visto en la entrada anterior, su naturaleza de objeto (de clase FuncionN) permite que sea pasada, asignada o retornada en el tipico estilo funcional.

scala> def m(x: Int) = 2*x
m: (x: Int)Int

scala> val f = (x: Int) => 2*x
f: (Int) => Int =

como podemos ver, el tipo para funcion y objeto difiere. (Antes hemos dicho que los metodos siempre pertencen a clases. Para poder definir metodos como hemos hecho aqui, el interprete de Scala crea un objeto invisible que rodea todo lo definido por linea de comandos.) ¿Que pasa si intentamos invocar toString sobre un metodo?

scala> m.toString
:7: error: missing arguments for method m in object $iw;
follow this method with `_' if you want to treat it as a partially applied function
m.toString       ^

scala> f.toString
res1: java.lang.String =

Efectivamente, el metodo no es un objeto, pero la funcion si.

En Scala existe una convencion por la cual la sintaxis nombre() se traduce en una llamada al metodo apply de aquello referido con nombre. Esto significa que la expresion funcion(…) realmente se traduce en funcion.apply(…), en virtud de que, como vimos, las funciones son objetos con este metodo especial. Asi que en el fondo, las llamadas a funciones en Scala acaban siendo «por debajo» ejecucciones de algun metodo apply definido sobre algun objeto.  La creacion de estos objetos ocurre de manera transparente; cuando en codigo definimos funciones, Scala crea los objetos correspondientes de manera automatica.

Aun hay algo mas. En Scala se pueden asignar las funciones a variables (me refiero en este caso tanto a var como a val). Reiterando, esto es una asignacion de un objeto tipo FuncionN a dicha variable. Pero tambien podemos asignar a una variable un metodo de una clase. Como por ejemplo

scala> def m(x: Int) = 2*x
m: (x: Int)Int

scala> val f = m _
f: (Int) => Int = <function1>

scala> f.toString
res4: java.lang.String = <function1>

Como vemos, el tipo de f es el mismo que vimos anteriormente para una funcion. Scala ha creado automaticamente un objeto cuya funcion apply llama al metodo m, y este es el objeto asignado a f. Tambien vemos en este ejemplo la sintaxis utilizada para convertir m en una funcion parcialmente aplicada:

scala> val f = m _

Si hubieramos escrito simplemente

scala> val f = m

Scala habria interpretado que lo que tratamos de hacer es asignar a f el resultado de la invocacion de m. Pero lo que intentamos hacer es asignar m en si. La sintaxis m _ dice «no evalues m, sino que dame como resultado de la expresion la funcion (una vez se ha hecho la conversion de metodo en funcion) en si. En Scala, las funciones parcialmente aplicadas son funciones a los que no se les ha proporcionado todos sus argumentos (en el ejemplo que hemos visto, ninguno), y que por tanto no tiene aun valor de retorno, sino caracter de funcion. Ejemplos en el siguiente post.

Referencias:

http://jim-mcbeath.blogspot.com/2009/05/scala-functions-vs-methods.html

Categorías: Fundamentos

La vida secreta de las funciones

Hasta el momento habiamos hablado de que en Scala, todos los valores, incluso los literales numericos como ‘1′, son de hecho objetos. Tambien habiamos hablado de que las funciones son tratadas tambien como valores: se pueden asignar, pasar como argumentos y retornar. Quizas entonces podriamos intuir que, si los valores son objetos, y las funciones son valores, entonces las funciones tambien son objetos. En efecto, esto es asi, las funciones en Scala son instancias de clases abstractas parametrizables definidas dentro del paquete scala.

Veamos un ejemplo

object Func {
   def x(s:String) : () => Unit = {
      () => {
         println(s)
      }
   }
}

Si pegamos este texto sin modificar dentro del interprete de scala:

scala> object Func {
 | def x(s:String) : () => Unit = {
 | () => {
 | println(s)
 | }
 | }
 | }
defined module Func

Ahora invoquemos Func.x, que, recordemos, devuelve una funcion:

scala> Func.x("Hello World")
res101: () => Unit = <function>

Vemos como el interprete nos dice que el resultado es  () => Unit = <function>, que significa, una instancia de funcion sin argumentos que no devuelve nada. Lo importante es que es una instancia, o sea, un objeto. Sabemos que todos los objetos (en la rama AnyRef) tienen metodos toString, asi que comprobemoslo:

scala> Func.x("Hello World").toString()
res112: java.lang.String = <function>

Entonces, de que clase es una instancia Func.x? Como habiamos dicho antes, el paquete scala define clases abstractas parametrizables de la que son instancias funciones. En este caso, Func.x es una instancia de scala.Function0[T]. Function0[T] representa funciones sin argumentos (o sea, cero argumentos, de ahi el 0), con tipo de retorno T. Por tanto, Func.x es realmente una instancia de Function0[Unit]. La clase Function0 (asi como Function1, Function2..) define un metodo especial, apply, que es donde realmente reside el codigo de la implementacion de la funcion. Veamoslo todo junto:

scala> object Func {
 | def x(s:String) : () => Unit = {
 | () => {
 | println(s)
 | }
 | }
 |
 | def y(s:String) : () => Unit = {
 | new Function0[Unit] {
 | def apply() {
 | print(s)
 | }
 | }
 | }
 | }
defined module Func

scala> Func.x("Hello World")
res113: () => Unit = <function>

scala> Func.y("Hello World")
res114: () => Unit = <function>

scala> Func.x("Hello World")()
Hello World

scala> Func.y("Hello World")()
Hello World
scala> Func.x("Hello World").apply()
Hello World

scala> Func.y("Hello World").apply()
Hello World

donde hemos definido otro metodo Func.y para definir manualmente la instancia de Function0, con identicos resultados. Tambien vemos como la invocacion de Func.x(«Hello World»)() y Func.x(«Hello World»).apply() son equivalentes.

Funciones y Metodos

Hasta el momento hemos utilizado los terminos funcion y metodo indistintamente. En la proxima entrega veremos, basandonos en lo expuesto en este post, la diferencia sutil entre estos dos mecanismos.

Categorías: Fundamentos

Funciones de orden superior

diciembre 9, 2009 Deja un comentario

Retornemos brevemente al mundo de HelloWorld. Pero esta vez, lo haremos mas complicado.


object HelloWorldFunc {
   def combine (f1: () => Unit, f2: () => Unit) : () => Unit = {
      () => {
         f1()
         f2()
      }
   }
   def Hello() {
      print("Hello ")
   }
   def World() {
      print("World!")
   }

   def main(args: Array[String]) {
      combine(Hello, World)()
   }
}

Aunque no es la manera optima de escribir Hello World! por pantalla este ejemplo incluye algunas caracteristicas importantes.

Recordemos nuestro uso del termino valor para referirnos a todo aquello que puede ser asignado, pasado como argumento o retornado de una funcion, o algo sobre lo que se opera. Pues bien, en Scala las funciones son valores. Esta caracteristica tan importante, y algunas  otras que no mencionare ahora, hacen de Scala un lenguaje hibrido oo-funcional[1].

Combinando

Vemos que en el ejemplo se definen dos funciones que escriben por pantalla, una para escribir «Hello«, y otra para escribir «World!«. La funcion mas interesante es combine, que recibe como argumentos dos funciones, y devuelve otra funcion: la compuesta for la ejecuccion de sus dos argumentos, uno despues del otro. Por tanto, para escribir Hello World!, la funcion main llama a combine pasandole Hello y World, y recibe como retorno una funcion que combina las dos. Este retorno, que es una funcion, es a su vez invocado, y es por esto que tenemos dos parentesis finales en esa linea

   def main(args: Array[String]) {
      combiner(Hello, World)()
   }

Se denominan funciones de orden superior (higher order functions) aquellas que reciben funciones como parametro o tienen como retorno una funcion. En este caso combine es una funcion de orden superior por partida doble, al tener las dos caracteristicas. La practica de programar utilizando funciones de orden superior se llama higher order programming[2], y es un estilo muy expresivo tipico de lenguajes funcionales.

Uso real

Aparte de para complicar codigo de HelloWorld, existen otros usos del estilo funcional. Un ejemplo tipico es la implementacion de una funcion generica de ordenamiento (sort) de lista[3]. Separar la problematica del ordenamiento en dos partes, una la que invoca un comparador para determinar la posicion de cada elemento, y otra el comparador en si, permite que la primera parte sea generica y abstraida de la naturaleza exacta de los elementos a ordenar. Asi, una funcion de ordenamiento generica recibe como parametro un comparador especializado, una funcion que establece el orden entre dos elementos de un tipo especifico.

Sintaxis de tipo funcion

La sintaxis argumentos => retorno sirve para definir un tipo funcion, con argumentos argumentos y retorno tipo retorno. En este caso tenemos que combine recibe dos argumentos, los dos funciones sin argumentos que tampoco retornan nada. A su vez, combine retorna una funcion sin argumentos que no retorna nada. Por tanto la sintaxis completa, tanto para los argumentos como para el retorno de combine es

.. (f1: () => Unit, f2: () => Unit) : () => Unit ..

Retorno, funciones anonimas y closures

En Scala no existe el equivalente a return para retornar de una funcion. Al estilo de los lenguajes funcionales, el retorno de una funcion en Scala es el resultante de evaluar la ultima expresion del cuerpo de la misma. Por tanto el retorno de combine es

() => {
    f1()
    f2()
}

lo cual define inline una funcion anonima cuyo comportamiento es invocar primero f1 y despues f2. Las funciones anonimas son un mecanismo importante que posibilitan la definicion agil y flexible de comportamiento alli donde sea necesario sin tener que declararlas anteriormente. Tipicamente son bloques de codigo utilizados en ambitos circunstritos del codigo, para lo que no hace falta una definicion ni una referencia para uso posterior. Un uso familiar para los que conozcan Java es como callbacks, aunque en Java,  al no existir las funciones de manera independiente, se tienen que empaquetan dentro clases anonimas, que hacen de meros vehiculos.

Tambien decir brevemente que esta funcion anonima hace de closure[4]. Notese que los argumentos f1 y f2 son accesibles por la funcion anonima aun cuando el ambito de combine ya ha expirado.

[1] Los lenguajes funcionales estan cobrando interes recientemente por su capacidad para expresar flujos de ejecuccion concurrente y asi aprovechar arquitecturas multi nucleo, por las que transitara la industria de los microprocesadores en los proximos años.

[2] http://en.wikipedia.org/wiki/Higher_order_programming

[3] http://www.scala-lang.org/docu/files/api/scala/util/Sorting$object.html

[4] http://en.wikipedia.org/wiki/Closure_(computer_science)

Categorías: Fundamentos

Operando

diciembre 7, 2009 Deja un comentario

En el ejemplo anterior vimos como los literales numericos como ‘1’ son objetos, a traves de un ejemplo en el que se invocaban sus metodos para realizar una suma


println(1.+(1))

En realidad, este ejemplo muestra dos caracteristicas de Scala. Primero, que todos los valores son objetos, pero tambien que todas las operaciones son invocaciones de metodos. Estas dos caracteristicas conforman lo que se llama en Scala un Unified Object Model, al unificar caracteristicas que en otros lenguajes se tratan de manera separada y especial.

En el ejemplo se muestra como la operacion +, en 1 + 1, es realmente una invocacion de metodos. Esto es cierto no solo para +, sino para todos los operadores que estemos acostumbrados a utilizar, existiendo metodos correspondientes. Veamos otro ejemplo

println((1.+(1)).==(2))

el resultado al ejecutar seria true, en este caso hemos invocado el operador == de igualdad. (Se han insertado unas parentesis adicionales por cuestiones de preferencia de los operadores)

Para evitar la sintaxis engorrosa de realizar las invocaciones explicitamente al usar operadores, Scala admite las expresiones 1 + 1 como version abreviada (syntactic sugar) de 1.+(1), de manera que podemos seguir programando como siempre, pero disfrutando del poder y elegancia que este tratamiento de los operadores confiere.

Al ser los operadores simples metodos, Scala permite al programador definir nuevos operadores sobre tipos, y asi poder crear DSL’s[1] con total libertad, que a su vez hacen posible escribir codigo de manera muy compacta y legible adaptandose al problema en cuestion. Esta facultad es una version mucho mas poderosa del operator overloading[2] de C++.

[1] http://en.wikipedia.org/wiki/Domain-specific_language

[2] http://en.wikipedia.org/wiki/Operator_overloading

Categorías: Fundamentos

Todo son objetos

diciembre 6, 2009 Deja un comentario

Llamemos valor a todo aquello que puede ser asignado, pasado a funciones y retornado, y modificado por operadores. Pues bien, en Scala, todos los valores son objetos[1], por lo que Scala es un lenguaje orientado a objetos puro, como Smalltalk[2]. Si escribimos


println(1 + 1)

en Java tendriamos que los ‘1’ son tipos primitvos int, y reciben un trato especial distinto de los objetos. En Scala esto no es asi, los dos ‘1’ son efectivamente objetos, asi como la expresion (1 + 1) que evalua a un objeto que es igual a dos. Son por tanto instancias de una clase, en este caso la clase Int. Para demostrar que ‘1’ es un objeto, veamos esta curiosa forma de escribir lo mismo


println(1.+(1))

No, no es una errata. Se esta invocando el metodo ‘+’ de la clase Int, de la cual es instancia ‘1’. Al ser ‘1’ un objeto como otro cualquiera podemos invocar sus metodos, con la habitual sintaxis objeto.metodo. En futuros posts veremos como Scala maneja los operadores, de nuevo siguiendo la filosofia de buscar tratamientos uniformes en detrimento de los casos particulares.

Jerarquia de Clases

Como en Scala todo los valores son objetos, todo posible valor estan representado en la jerarquia de clases. Esta jerarquia tiene como clase de nivel maximo a Scala.Any, como podemos ver[3]

Desde Scala.Any la jerarquia se divide en dos. Por un lado estan las clases de valor, los valores primitivos (usando el termino de Java), que extienden de Scala.AnyVal, y por otro lado las clases de referencia, lo que normalmente considerariamos objetos, extendiendo de Scala.AnyRef. Esta rama de la jerarquia es donde desarrollamos nuevas clases. Scala.AnyRef es por tanto la analoga de java.lang.Object.

Scala.Null y Scala.Nothing

En las oscuras profundidades de la jerarquia nos encontramos con Scala.Null y Scala.Nothing. Scala.Null tiene una unica instancia, el null de siempre; la ausencia de asignacion para una referencia. Como Scala.Null esta en la rama de Scala.AnyRef, no puede ser asignado a un Scala.AnyVal, lo cual tiene sentido, un Int no puede ser null, como tambien ocurre en Java.

Por otro lado, Scala.Nothing no tiene ninguna instancia. ¿Para que sirve entonces? Pues solo sirve como argumento de parametrizacion (p.e. List[Nothing], que definiria una lista vacia)  aunque en uso normal seguramente no lo necesitaremos.

[1] en el sentido de orientacion a objetos, no en el sentido object como palabra clave que declara Singletons

[2] http://en.wikipedia.org/wiki/Smalltalk

[3] http://www.scala-lang.org/node/128

Categorías: Fundamentos

Hello Scala! III: Detalles finales

diciembre 2, 2009 Deja un comentario

Es posible que algun lector haya echado de menos el tipado de la funcion main, que en Java habria sido


public static void main(String[] args)

mientras que en nuestro ejemplo teniamos


def main(args: Array[String])

En realidad, la sintaxis que utilizamos es una abreviacion, equivalente a


def main(args: Array[String]) : Unit = { ... }

Recordemos que en Scala el tipado se especifica con : tipo, asi que el tipo de main es Unit. Unit es el equivalente (aproximadamente) a void en Java y otros lenguajes[1]. Estrictamente hablando, hay una diferencia, y es que una objeto de tipo Unit si puede existir, con el unico valor posible (). Podemos ver Unit como el tipo Tupla-0, esto es una tupla de cero elementos cuyo unico valor, por tanto, es ().

Otro punto de entrada

Como en Java, el punto de entrada de un programa es una funcion main definida en un object. A diferencia de Java, scala tambien ejecutara el cuerpo de una clase si esta extiende de Application. Podriamos entonces haber escrito nuestro ejemplo asi:

object HelloWorld2 extends Application {
 println("Hello, world!")
}

Puede resultar un poco extraño la ejecuccion del cuerpo de una clase, pero como veremos en futuras entregas, el cuerpo de una clase no deja de ser un bloque de codigo que asigna valores a ciertos campos, sean estos variables (atributos) o funciones (metodos). No tiene pues, naturaleza exlcusivamente declarativa, como ocurre en Java.

Correspondencia archivo-clase

Finalmente apuntar que en Scala no es necesario que las clases esten definidas en archivos con el mismo nombre; podiamos haber escrito nuestro ejemplo en un archivo llamado Test.scala. Esta convencion, aunque seguramente util, no la impone el compilador.

[1] http://en.wikipedia.org/wiki/Unit_type

Categorías: Fundamentos

Hello Scala! II: Compilar y ejecutar

diciembre 1, 2009 Deja un comentario

En la ultima entrega nos quedamos a las puertas de compilar y ejecutar nuestro proyecto faraonico, HelloWorld. Salvamos nuestro texto en un archivo llamado HelloWorld.scala. El compilador se llama scalac, asi que

>scalac HelloWorld.scala

y para ejecutar

>scala HelloWorld

El comando scala busca clases en el directorio actual por defecto, existiendo la opcion -classpath igual que en Java. Curiosamente, el resultado de la compilacion scalac genera dos archivos .class, uno de ellos con un ‘$’ al final. Recordemos que scalac genera codigo para la JVM, con lo que el compilador tiene que hacer ciertos ajustes para acomodar las capacidades expresivas de Scala a las limitaciones de una maquina virtual diseñada para un lenguaje distinto. Si ejecutamos javap podemos ver que la JVM requiere dos clases para acomodar la doble naturaleza (desde la perspectiva de Java) que en Scala tiene object como Singleton; por un lado tenemos la clase con metodos staticos, y por otro la unica instancia de esa clase con metodos asociadas a la clase.


>javap HelloWorld$
Compiled from "HelloWorld.scala"
public final class HelloWorld$ extends java.lang.Object implements scala.ScalaObject{
 public static final HelloWorld$ MODULE$;
 public static {};
 public HelloWorld$();
 public void main(java.lang.String[]);
 public int $tag()       throws java.rmi.RemoteException;
}

>javap HelloWorld
Compiled from "HelloWorld.scala"
public final class HelloWorld extends java.lang.Object{
 public static final void main(java.lang.String[]);
 public static final int $tag()       throws java.rmi.RemoteException;
}

Si por alguna casualidad ejecutaramos el comando scala sin parametros, por ejemplo para ver sus opciones, no sorprenderia con


>scala
Welcome to Scala version 2.7.7.final (Java HotSpot(TM) Client VM, Java 1.6.0_02).
Type in expressions to have them evaluated.
Type :help for more information.

scala>

Asi que el comando scala es tambien un interprete, al estilo de un lenguaje dinamico, en el que podemos insertar codigo para su ejecuccion interactiva.  Si le pasamos como argumento un fichero con codigo ya compilado entonces lo ejecuta directamente, el equivalente a java.

La faceta de scala como interprete abre muchas posibilidades de uso en scripts[1], como si de un lenguaje dinamico se tratara, evitando aparantemente el paso de compilacion, que se realiza «por debajo» de manera transparente. Claro esta que en algun momento se lanzara una JVM con su tiempo de inicializacion y uso de memoria correspondientes, pero es un precio a pagar por la posibilidad de disponer de toda la potencia de Scala (y todas las librerias existentes de Java) en un contexto de script.

[1] http://www.codecommit.com/blog/scala/scala-as-a-scripting-language

Categorías: Fundamentos

Hello Scala!

noviembre 30, 2009 Deja un comentario

Como no podia ser de otra forma, empezamos con el ejemplo canonico, Hello World, que imprime por pantalla un bonito mensaje:

object HelloWorld {
   def main(args: Array[String]) {
      println("Hello, world!")
   }
}

Existen ya en este minimo ejemplo varias cuestiones dignas de mencion, en orden de aparicion

  • La palabra clave object define un Singleton, una clase con una unica instancia
  • Las funciones (o metodos) se definen con la palabra clave def
  • El tipo de una variable se define con la sintaxis nombre : tipo (en lugar de tipo nombre en Java)
  • Los arrays se definen con Array[Tipo], parametrizando la clase Array. La sintaxis de parametrizacion utiliza corchetes [], en lugar de los angulos <> de Java

De estas cuestiones, las mas significativas son la primera y la ultima. Primero, en Scala no existen los metodos estaticos. El equivalente son los metodos de un object. Al estar estos ligados a una unica instancia, hacen las veces de metodos ligados a una clase. Si se quieren combinar metodos asociados al Singleton y metodos asociados a la clase, se declara tanto una clase como un object con el mismo nombre.

En segundo lugar, podemos ver que Scala no contempla de manera especial los arrays, solventando elegantemente su tratamiento a traves de la parametrizacion de la clase Array. En Scala el acceso a un array es implementado por una funcion normal y corriente. Esta funcion es parte de la clase Array y por tanto puede ser parametrizada para cada posible tipo de array.

Segun vayamos explorando Scala veremos que los tratamiento unificados en detrimento de los casos particulares es un estilo recurrente de este lenguaje. Al principio puede resultar poco intuitivo si estamos acostumbrados a estos tratamientos especiales, por ejemplo en Java, pero  segun uno se acostumbre ve el sentido y la elegancia de esta filosofia.

Finalmente, para que no quede nada en el tintero, mencionar brevemente que la referencia a String es de hecho una referencia a java.lang.String, la interaccion con Java es transparemente. Scala importa por defecto todos los miembros del paquete java.lang. En otros posts veremos mas en detalle esta interactuacion con Java; es desde luego uno de los puntos fuertes de Scala para lograr adopcion.

Ah, se me olvidaba. El que eche en falta en el ejemplo el dichoso punto y coma ‘;‘ al final de println(..) puede olvidarse de el. En Scala no es necesario, aunque el compilador no protesta si los ve.

Categorías: Fundamentos